Vue: 进阶构造属性
之前在Vue: 构造选项 里罗列了options 的五类属性,今天就把剩下的关于资源和组合的属性说一说。
directives、 mixins、 extends、 provide、 inject
directives - 指令
自定义指令
- 我们已经学了一些内置指令
如 v-if、 v-for、 v-show、 v-html等
- 今天学习如何自己造一个指令
目标: 造出v-x, 点击即出现一个x
两种写法
- 声明一个全局指令
Vue.directive("x", directiveOptions);
这样你就可以在任何组件里用v-x了,动手试试
- 声明一个局部指令
new Vue({
...,
directives: {
"x": directiveOptions
}
})
注意,v-x只能用在该实例中,动手试试
directiveOptions
上面例子中的directiveOptions里有哪些属性?
五个函数属性
- bind(el, info, vnode, oldVnode) - 类似created
- inserted(参数同上) - 类似mounted
- updated(参数同上) - 类似updated
- componentUpdated(参数同上) - 用得不多,见文档
- unbind(参数同上) - 类似destroyed
举例
main.js
import Vue from "vue/dist/vue.js"; // 使用完整版
Vue.config.productionTip = false;
new Vue({
directives: {
on2: {
// bind 可以改为 inserted
bind(el, info) {
el.addEventListener(info.arg, info.value);
// Vue 自带的 v-on 并不是这样实现的,它更复杂,用了事件委托
},
unbind(el, info) {
el.removeEventListener(info.arg, info.value);
},
},
},
template: `
<button v-on2:click="hi">点我</button>
`,
methods: {
hi() {
console.log("hi");
},
},
}).$mount("#app");
缩写
- directiveOptions在某些条件下可以缩写为函数,用得不多,可以自行看文档
directives - 指令 的作用
主要用于 DOM 操作
- Vue 实例/组件用于数据绑定、事件监听、DOM更新
- Vue 指令主要目的就是原生DOM操作
减少重复
- 如果某个DOM操作你经常使用,就可以封装为指令
- 如果某个DOM操作比较复杂,也可以封装为指令
mixins - 混入
混入其实就是复制
减少重复
类比
- directives的作用是减少DOM操作的重复
- mixins的作用是减少data、 methods、 钩子的重复
所有在构造选项里放的东西都可以放到mixins里来,真香
场景描述
假设我们需要再每个组件上添加name和time,在created和destroyed时,打出提示,并报出存活时间。一共有五个组件,请问你怎么做?
- 给每个组件添加data和钩子,共五次
- 或者使用mixins减少重复
- 完整代码:
log.js
const log = {
data() {
return {
name: undefined,
time: undefined,
};
},
created() {
if (!this.name) {
throw new Error("need name");
}
this.time = new Date();
console.log(`${this.name}出生了`);
},
beforeDestroy() {
const now = new Date();
console.log(`${this.name}死亡了,共生存了 ${now - this.time} ms`);
},
};
export default log;
Child1.vue
<template>
<div>Child1</div>
</template>
<script>
import log from "../mixins/log.js";
export default {
data() {
return {
name: "Child1",
};
},
created() {
console.log("Child 1 的 created");
},
mixins: [log],
};
</script>
log.js和Child1.vue会智能合并,所以叫mixins
mixins 技巧
- 选项智能合并
- Vue.mixin
见文档,不推荐使用,因为容易出现范围过大的问题
extends - 继承
减少重复
- 还是与mixins同样的需求
Q: 这次我不想要在每个组件上都写一个mixins,有什么办法吗?
A: 你可以使用Vue.extend或options.extends
MyVue.js
import Vue from "vue";
const MyVue = Vue.extend({
data() {
return {
name: undefined,
time: undefined,
};
},
created() {
if (!this.name) {
throw new Error("need name");
}
this.time = new Date();
console.log(`${this.name}出生了`);
},
beforeDestroy() {
const now = new Date();
console.log(`${this.name}死亡了,共生存了 ${now - this.time} ms`);
},
});
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>
extends是比mixins更抽象一点的封装
如果你嫌写五次mixins麻烦,可以考虑extends一次,不过实际工作中用得很少
平时我还是基本用mixins,extends很少用到
provide 和 inject - 提供和注入
使用举例
需求
App.vue
<template>
<div :class="`app theme-${themeName} fontSize-${fontSizeName}`">
<Child1 />
<button>x</button>
</div>
</template>
<script>
import Child1 from "./components/Child1.vue";
export default {
name: "App",
provide() {
return {
themeName: this.themeName,
fontSizeName: this.fontSizeName,
changeTheme: this.changeTheme,
changeFontSize: this.changeFontSize,
};
},
data() {
return {
themeName: "blue", // 'red'
fontSizeName: "normal", // 'big' | 'small'
};
},
methods: {
changeTheme() {
if (this.themeName === "blue") {
this.themeName = "red";
} else {
this.themeName = "blue";
}
},
changeFontSize(size) {
if (["normal", "big", "small"].indexOf(size) === -1) {
throw new Error(`wront size: ${size}`);
}
this.fontSizeName = size;
},
},
components: {
Child1,
},
};
</script>
<style></style>
ChangeThemeButton.vue
<template>
<div>
<button @click="changeTheme">换肤</button>
<button @click="changeFontSize('big')">大字</button>
<button @click="changeFontSize('small')">小字</button>
<button @click="changeFontSize('normal')">正常字</button>
</div>
</template>
<script>
export default {
inject: ["themeName", "changeTheme", "changeFontSize"],
};
</script>
Child1.vue
<template>
<div>
Child 1
<change-theme-button />
<!-- 这样写是可以的,也可以找到ChangeThemeButton这个组件 -->
</div>
</template>
<script>
import ChangeThemeButton from "./ChangeThemeButton.vue";
export default {
components: {
ChangeThemeButton,
},
};
</script>
- 祖先栽树(provide),后人乘凉(inject)
总结
- 作用: 大范围的 data 和 method 等共用
- 注意: 不能只传 themeName 不传 changeTheme,因为 themeName 的值是被复制给 provide 的
思考题: 传引用可以吗?
答: 可以,但是不推荐,因为容易失控
总结
directives 指令
- 全局用
Vue.directive('x', {...})
- 局部用
options.directives
- 作用是减少DOM操作相关重复代码
mixins 混入
- 全局用
Vue.mixin({...})
- 局部用
options.mixins:[mixin1, mixin2]
- 作用是减少options里的重复
extends 继承/扩展
- 全局用
Vue.extend({...})
- 局部用
options.extends:{...}
- 作用跟mixins差不多,只是形式不同
provide / inject 提供和注入
- 祖先提供东西,后代注入东西
- 作用是大范围、隔 N 代共享信息