这篇文章主要记录一下Vue的一些进阶属性:
- directives(指令)
- mixins (混入【实际就是复制】)
- extends (继承 【其实也是复制】)
- provide (提供)
- inject (注入)
Directives
除了内置指令:v-if, v-for, v-show, v-model外,Vue也允许我们自己注册自定义指令
例子:当页面加载一进来,输入框就属于聚焦状态
// 使用
<input v-focus />
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到DOM中时...
inserted: function(el) {
// el就是当前指令绑定的元素
el.focus()
}
})
// 在组件中注册局部指令
directives: {
focus: {
inserted: function(el) {
el.focus()
}
}
}
钩子函数
一个指令定义对象,提供了以下几个钩子函数:
bind: 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置(相当于updated)inserted: 被绑定的元素插入到DOM时调用(相当于mounted)update: 所在组件的VNode更新时调用,但是可能发生在其子VNode更新前componentUpdated: 指令所在组件的VNode及子VNode全部更新后调用unbind: 只调用一次,指令与元素解绑时调用 (相当于destroyed)
常用的可能是:bind, inserted, unbind这三个
钩子函数的参数(el, binding, vnode, oldVnode)
指令钩子函数会被传入以下参数
el: 指令所绑定的元素,可以用来直接操作DOMbinding:一个对象,包含以下 property:name:指令名,不包括v-前缀。value:指令的绑定值,例如:v-my-directive="1 + 1"中,绑定值为2。oldValue:指令绑定的前一个值,仅在update和componentUpdated钩子中可用。无论值是否改变都可用。expression:字符串形式的指令表达式。例如v-my-directive="1 + 1"中,表达式为"1 + 1"。arg:传给指令的参数,可选。例如v-my-directive:foo中,参数为"foo"。modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar中,修饰符对象为{ foo: true, bar: true }。
vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。oldVnode:上一个虚拟节点,仅在update和componentUpdated钩子中可用。
自定义一个v-on的方法
<template>
<div id="app">
<button v-on2:click="handle">点击执行handle函数</button>
</div>
<script>
export default {
directives: {
on2: {
inserted(el, binding) {
// 打印binding的参数
//name: "on2"
//rawName: "v-on2:click"
//value: ƒ bound handle() {}
//expression: "handle"
//arg: "click"
//modifiers: Object
//def: Object
// 由此可见, arg就是事件名,value就是要执行的函数
// 给元素绑定点击事件
el.addEventListener(binding.arg, binding.value);
},
// 页面卸载时解绑该事件
unbind(el, binding) {
el.removeEventListener(binding.arg, binding.value);
}
}
},
methods: {
handle() {
console.log('点击了按钮')
}
}
}
</script>
</template>
指令的作用
-
主要用于DOM操作
- Vue实例/组件用于数据绑定、事件监听、DOM更新
- Vue指令主要目的就是原生DOM操作
-
减少重复
- 如果某个DOM操作你经常使用,就可以封装为指令
- 如果某个DOM操作比较复杂,也可以封装为指令
实际上指令就是减少对DOM的重复操作
mixins
mixins: 混入,实际上就是复制的意思
-
类比
- directives的作用是减少DOM操作的重复
- mixins的作用是减少data、methods、钩子等的重复
-
场景描述
- 假设我们需要在每个组件上添加name和time
- 在created、beforeDestroy时,打出提示,并报出存活时间
- 一共五个组件,请问怎么做
- 一: 给每个组件添加data和钩子,共五次
- 二: 使用mixins减少重复
App.vue
<template>
<div id="app">
<Child1 v-if="child1Visible" />
<button @click="handle('child1Visible')">X</button>
<Child2 v-if="child2Visible" />
<button @click="handle('child2Visible')">X</button>
<Child3 v-if="child3Visible" />
<button @click="handle('child3Visible')">X</button>
<Child4 v-if="child4Visible" />
<button @click="handle('child4Visible')">X</button>
<Child5 v-if="child5Visible" />
<button @click="handle('child5Visible')">X</button>
</div>
</template>
<script>
import Child1 from "@/components/Child1.vue";
import Child2 from "@/components/Child2.vue";
import Child3 from "@/components/Child3.vue";
import Child4 from "@/components/Child4.vue";
import Child5 from "@/components/Child5.vue";
export default {
name: "App",
components: {
Child1,
Child2,
Child3,
Child4,
Child5,
},
data() {
return {
child1Visible: true,
child2Visible: true,
child3Visible: true,
child4Visible: true,
child5Visible: true,
};
},
methods: {
handle(params) {
this[params] = !this[params];
},
},
};
</script>
这是app.vue里的内容,引入五个子组件,点击对应的按钮显示隐藏对应的子组件,这样就能出发created和beforeDistroy的钩子函数
child1.vue 内容
<template>
<div>Child1</div>
</template>
<script>
export default {
data() {
return {
name: "Child1",
beginTime: null,
};
},
created() {
this.beginTime = Date.now();
console.log(`我是${this.name}诞生了`);
},
beforeDestroy() {
let diffTime = Date.now() - this.beginTime;
console.log(`我是${this.name}结束了, 存活了${diffTime} ms`);
},
};
</script>
那么:如果要实现上面的场景,五个子组件里都得加上Child1.vue一样的内容。
太多重复的了吧....
用mixins实现
父组件不变,创建一个mixins文件夹
myMixin.js
export default {
data() {
return {
name: undefined,
beginTime: null,
};
},
created() {
if (!this.name) {
throw new Error("name is nodefined");
}
this.beginTime = Date.now();
console.log(`我是${this.name}诞生了`);
},
beforeDestroy() {
let diffTime = Date.now() - this.beginTime;
console.log(`我是${this.name}结束了, 存活了${diffTime} ms`);
},
};
Child1.vue
<template>
<div>Child1</div>
</template>
<script>
import myMixin from '@/mixins/myMixin.js'
export default {
mixins: [myMixin],
data() {
return {
name: "Child1",
};
},
};
</script>
- 创建一个mixin的js文件,把重复的data, methods, 钩子等传入进去,然后在需要调用的文件里,调用
mixins:[]就行了 - mixins就是把这部分公用的智能导入到组件里
所以mixins实际上就是复制
extends
- extends: 继承
- 它是比mixins更抽象一点的封装
- 场景跟mixins一样,接着上面的代码,在src目录上创建一个js文件
MyVue.js
import Vue from "vue";
import myMixin from '@/mixins/myMixin.js'
const MyVue = Vue.extend({
mixins: [myMixin]
});
export default MyVue
Child1.vue
<template>
<div>Child1</div>
</template>
<script>
import MyVue from '@/MyVue.js'
export default {
extends: MyVue,
data() {
return {
name: "Child1",
};
},
};
</script>
总结: 个人感觉跟mixins差不多,都是复制,除非有多个mixins,才用extends,否则感觉用mixins就可以了
provide和inject
提供和注入
-
功能: 父组件里的属性和方法,在子孙组件里面调用
-
场景: 换肤场景
-
三个文件: app.vue, Child1.vue, Button.vue
-
app.vue文件
<template>
<div id="app" :class="`app theme-${themeName}`">
<Child1 />
</div>
</template>
<script>
import Child1 from "@/components/Child1.vue";
export default {
name: "App",
components: {
Child1,
},
// 提供data和method可以让子孙组件们可以调用
provide() {
return {
themeName: this.themeName,
changeTheme: this.changeTheme,
};
},
data() {
return {
themeName: "blue",
};
},
methods: {
changeTheme() {
this.themeName = this.themeName == "blue" ? "red" : "blue";
},
},
};
</script>
<style>
.app {
width: 100%;
height: 100vh;
}
.app.theme-blue {
background: blue;
}
.app.theme-red {
background: red;
}
</style>
这是app.vue,引入Child1.vue子组件,换肤主要是:class="`app theme-${themeName}`"这个代码,改变变量themeName,依靠css来换背景
- Child1.vue文件
<template>
<Button />
</template>
<script>
import Button from "@/components/Button.vue";
export default {
components: {
Button,
},
};
</script>
Child1.vue这个文件只是调用了Button.vue文件,没有别的操作
- Button.vue文件
<template>
<div>
<button @click="changeTheme">换肤,当前{{ themeName }}色</button>
</div>
</template>
<script>
export default {
inject: ["themeName", "changeTheme"],
};
</script>
- 通过
inject注入可以获取到app.vue中provide的变量和方法 - 这里的
themeName这个变量是一个字符串,它和app.vue里面的themeName 没有任何关系,即使在Button.vue里面改变这个变量,app.vue里面的这个变量也不会变。。。在app.vue里面改变themeName变量,Button.vue里的这个themeName变量不会跟着改变 - 那么在Button.vue里面只能调用
themeName这个变量,怎么修改app.vue里的themeName这个变量呢? - 在app.vue里定义个
changeTheme函数修改themeName变量,然后在Button.vue里面调用changeTheme函数
总结
-
directives 指令
- 全局用Vue.directive('X', {...})
- 局部用options.directives
- 作用是减少DOM操作的相关重复代码
-
mixins 混入
- 全局用Vue.mixin({...})
- 局部用options.mixins: [mixin1, mixin2]
- 作用是减少options里的重复
-
extends 继承/扩展
- 全局用Vue.extend({...})
- 局部用options.extends: {...}
- 作用跟mixins差不多,只是形式不同
-
provide / inject 提供和注入
- 祖先提供东西,后代注入东西
- 作用是大范围,隔N代共享信息