1、构造选项 options
options 是创建 vue 实例时的参数,具体可以查看 选项官方文档,options 内含 五大类 属性:
数据:data、props、propsData、computed、methods、watch
DOM:el、template、render、renderError
生命周期钩子:beforeCreate/created、beforeMount/mounted、beforeUpdate/updated、activated/deactivated、beforeDestroy/destroyed、errorCaptured
资源:directives、filters、components
组合:parent、mixins、extends、provide、inject
红色代表基础重要属性,黄色代表高级重要属性,紫色代表特殊的重要属性,其余的到官网做一些了解即可。关于 Vue 的 options 选项的基础重要属性,可以参考这篇 博客。下面重要说一下 Vue 的 options 选项的高级重要属性:
2、computed 属性 - 计算属性
计算属性实为方法,但是可以被当作属性使用,它是根据其他属性计算而来的结果,使用的时候不需要加括号,Vue 会自动读取该函数的返回值,它会根据依赖是否变化来缓存。官网示例 如下:
<div id="example"> <p>Original message: "{{ message }}"</p> <!--Hello--> <p>Computed reversed message: "{{ reversedMessage }}"</p> <!--olleH--> </div> <script> var vm = new Vue({ el: '#example', data: { message: 'Hello' }, computed: { reversedMessage: function () {//计算属性的getter,要设置getter和setter则reversedMessage{ get(),set() } return this.message.split('').reverse().join(''); // `this` 指向 vm 实例 } } }) </script>
在 reversedMessage 计算属性中,我们提供的函数将用作属性 vm.reversedMessage 的 getter 函数,用户可以打开浏览器的控制台,自行修改例子中的 vm。 vm.reversedMessage 的值始终取决于 vm.message 的值。
计算属性有缓存,如果依赖的属性没有变化,就不会重新计算。getter/setter 默认不会做缓存,Vue 做了特殊处理。
3、watch 属性 - 侦听/监听
当 Vue 实例上的 data 数据发生时,执行一个函数。监听可以完美实现一个撤销历史数据的功能。
注意:watch 是异步的,等对应的操作都执行完了,才会去执行 watch 监听。$nextTick()
undo (){ this.inUndoMode = true; //撤销模式进行如下操作 this.n = old; //watch 是异步的 this.$nextTick(() => { //类似setTimeout(),在异步watch执行之后再执行 this.inUndoMode = false; //撤销操作被监听到后,将撤销模式置为 false },0) }
注意:watch 有一个 immediate 属性。当 data 数据第一次创建到页面渲染的过程是不会被监听的,它认为数据从无到有的过程不算是真正意义上的数据改变的过程,如果想让 watch 监听数据从无到有的过程,则需要 immediate 属性:
watch:{ //如果是一级属性的话:user(){}、二级:'user.email'(){} 'user.email':{ //如果要监听this.user.email从无到有的过程,则需将函数切换为对象{} handler(){ //将数据变化后的操作放到 handler(){} 中、 并设置 immediate 属性 const { user:{ email, nickname, phone}} = this; //结构赋值 this. displayName = nickname || email || phone; }, immediate: true; //监听this.user.email数据从无到有的过程 } }
注意:watch 还有一个 deep 属性。在 Vue 的 data 内部数据中,有的可能是对象类型,比如下面示例,当改变 animal.name 的值时,监听的 animal 的函数并不会执行,因为 animal 的内存地址没变,Vue 就认为 animal 属性没有发生变化。如果希望内部属性变化也要 watch 监听到,则需要设置 deep 属性。
data: { num: 1, //boolean类型 animal: { //对象类型 name: '二哈' } }, watch: { num(){ console.log( "num 变了"); }, //设置 this.num=2,就会执行该监听函数 //animal(){ console.log( "animal 变了"); }, //设置 this.animal = { name: '二哈' }会执行这行(对象地址变了) animal:{ handler(){ console.log( "animal 变了"); }, deep: true //表示,animal对象的内部属性变化也会触发animal的watch,有了它就不需要挨个监听其内部属性了 }, "animal.name": function(){ console.log("animal.name变了"); } //执行this.animal.name='金毛'会执行该监听函数 } //但是上一条并不会执行,因为Vue认为animal属性没变
补充:Vue 的默认设置是,如果给 Vue 中的对象类型的 data 数据原封不动的赋值给它,Vue 就会认为该对象变了,但内部属性没变。如果只改变对象类型数据的内部属性,则 Vue 认为该对象没变,就不会执行监听函数操作。如果希望 Vue 监听到对象类型数据的内部属性变化,也认定为该对象也变化了,可以设置 deep 属性。
补充:watch:{} 属性中的监听函数不能使用 ()=> {} 箭头函数,可用 n:function(){} 的形式,或 n(){} 的形式等。因为箭头函数中的 this 是全局对象,不是指 vm 实例对象。
//语法 1 watch: { o1: () => {}, //别用这种,这里的this是全局对象 o2: function(value,oldValue){}, o3(){}, o4: [ fun1,fun2 ], //这里的函数是在 methods 中声明好的 o5: 'methodName', o6: { handler: fun, deep: true, immediate: true }, 'object.a': function(){} //监听对象类型数据的内部属性 } //语法 2 - 在 vue 实例的 options 之外使用这种方式: vm.$watch('xxx', fun, {deep: .., immediate: ..} //监听 xxx //或者在 created() 钩子中使用下面这种形式 created(){ this.$watch('xxx', fun, {deep: .., immediate: ..} //监听 xxx }
4、directives 属性 - 指令
我们在使用 Vue 的时候基本上不会涉及到 DOM 操作,但是有的情况下,开发人员仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令,将 DOM 操作封装成一个 directive 指令,让它来进行原生 DOM 操作。
Vue 提供了很多内置指令,一般是以 v- 开头,比如: v-if/v-for/v-bind/v-on 都是指令,除了这些之外,用户还可以自定义指令,感兴趣的话可以参考 Vue 官方文档,下面我们自己创建一个指令,创建方式又有两种:
//一、声明一个全局指令 - 可在项目中任意组件内标签使用 Vue.directive('x', directiveOptions) //1、指令名、2、指令选项 声明后可在任何组件的标签上用 v-x 指令 Vue.directive('x', { inserted: function(el){ //inserted 当被绑定的元素 el 插入到 DOM 中时…… 使用:<div v-x>click</div> el.addEventListener('click',()=>{console.log('xxx')}) } }) //二、声明一个局部指令 - 仅限组件内标签使用 new Vue({ ···, directives: { "x": directiveOptions } //v-x只能用在该实例中 }) new Vue({ ···, directives: { "x": inserted: function(el){ //inserted 当被绑定的元素 el 插入到 DOM 中时…… 仅限组件内便签使用 el.addEventListener('click',()=>{console.log('xxx')}) } } })
一个指令定义对象可以提供如下几个钩子函数 (均为可选):(1) bind - 类似created、(2) inserted - 类似mounted、(3) update - 类似updated、(4) componentUpdated、(5) unbind - 类似 destroyed。具体可查看 Vue 官方文档 ,模仿 v-on 例子
指令的作用:Vue 的实例或组件主要封装了对数据绑定、事件监听、DOM操作的一些函数,这个 directive 指令属性主要的目的就是封装一些需要的原生 DOM 操作。开发人员可以将某个经常用到的或复杂的 DOM 操作封装为指令,以减少重复 DOM 操作。
5、mixins 属性 - 混入
mixins 混入说白了就是复制,即将别的组件的构造选项复制到另一个组件中,这样可以减少重复性。比如你在工作中需要开发五个组件,这五个组件中需要同样的 data、methods 等构造选项,那你不需要在五个组件中分别写上同样的 data/ methods,而是使用 mixins 属性。类比 directives 指令,directives 的作用是减少 DOM 操作的重复,mixins 的作用是减少 data、methods、钩子的重复。附上 Vue 官方文档
mixins 选项接收一个混入对象的数组。这些混入对象可以像正常的实例对象一样包含实例选项,这些选项将会被合并到最终的选项中,使用的是和 Vue.extend() 一样的选项合并逻辑。也就是说,如果你的混入包含一个 created 钩子,而创建组件本身也有一个,那么两个函数都会被调用。
Mixin 钩子按照传入顺序依次调用,并在调用组件自身的钩子 之前 被调用。
// 一般会在项目中新建一个 mixins/log.js 文件,用来些需要混入到组件中的代码 export default log = { data(){ return { num: 1 }} created: function () { console.log(num) } } // 在需要使用混入文件的组件中需要引入,并将其传入到 mixins 构造选项中 import log from './mixins/log.js' var vm = new Vue({ created: function () { console.log(2) }, mixins: [log] }) // => 1 Mixin 钩子按照传入顺序依次调用,并在调用组件自身的钩子之前被调用。 // => 2
上述这种方式是相当于局部添加 mixins 功能,除此之外还可以使用 Vue.mixins({ }) 来设置全局混入,这样在全局中的组件中不需要手动 mixins 引入,每个组件自动具备全局混入的属性,但是 不推荐 这种使用方式。
6、extend 属性 - 继承
如上同样的需求:你在工作中需要开发五个组件,这五个组件中需要同样的 data、methods 等构造选项,那你不需要在五个组件中分别写上同样的 data/ methods,而是使用 mixins 属性。除了使用 mixins 之外还可以使用 extends 属性。官方文档
<div id="mount-point"></div> // 创建构造器,Profile.js,然后将 Profile export 出去 var Profile = Vue.extend({ template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>', data: function () { //这里也可以用mixins属性 return { firstName: 'Walter', lastName: 'White', alias: 'Heisenberg' //或者在其他组件中导入,通过extends使用 } import Profile from './Profile.js' } export default { }) data: ..., // 创建 Profile 实例,并挂载到一个元素上。 extends: Profile new Profile().$mount('#mount-point') }
个人感觉 extends 是比 mixins 更抽象一点的封装,如果你嫌写五次 mixins 麻烦,可考虑 extends 一次。
7、provide/inject - 提供/注入
procide/inject 这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。关于 procide/inject 的 Vue 官方文档
provide 选项应该是一个对象或返回一个对象的函数,该对象包含可注入其子孙的属性。
// 祖级组件提供 'color' 属性及改变该属性的函数 'change' export default { name: 'app', data(){ return { color: bule } }, methods:{ change(){ this.color = red; } } provide(){ //将自身组件中的 color 复制一份提供给其他组件 return { colorName: this.color, //提供给别的组件的属性,别的组件不能擅自做修改 colorChange: this.change //需要再提供一个改变该属性的函数给其他组件 } } } // 子孙级组件注入 'color'、'change' //<div @click='usechange'>当前颜色为:{{colorName}}</div> export default { inject: ['colorName', 'colorChange'], //接收祖先组件提供的属性和方法 methods: { usechange(){ this.colorChange() //调用祖先组件提供的方法改变祖先组件提供的属性值 } } }
如果你觉得使用这种调用父级组件中的方法改变父级组件中的属性的方式比较麻烦的话,你可以在父级组件中的 provide() 中直接将要传递给子组件的属性设置为一个对象,比如:`colorName:{value:this.color}` 的形式,这样子组件就可以直接通过 ` this.colorName.value = "red" ` 即可。
注意:但是这种方式不推荐使用,不能只传 colorName属性 不传 colorChange 函数,因为 colorName 的值是被复制给provide 的。如果上述这种通过引用的方式传递属性,则容易失控。