Vue 之 Mixins (混入)和extend
Vue 之 Mixins (混入)
Mixins
Mixins是一种分发Vue组件中可复用功能的非常灵活的一种方式。
什么时候使用Mixins
页面的风格不用,但是执行的方法和需要的数据类似,我们是选择每个都写呢还是提取出公共部分呢?
基础实例
我们有一对不同的组件,它们的作用是切换一个状态布尔值,一个模态框和一个提示框。这些提示框和模态框除了在功能上,没有其他共同点:它们看起来不一样,用法不一样,但是逻辑一样。
// 模态框
const Modal = {
template: '#modal',
data() {
return {
isShowing: false
}
},
methods: {
toggleShow() {
this.isShowing = !this.isShowing;
}
},
components: {
appChild: Child
}
}
// 提示框
const Tooltip = {
template: '#tooltip',
data() {
return {
isShowing: false
}
},
methods: {
toggleShow() {
this.isShowing = !this.isShowing;
}
},
components: {
appChild: Child
}
}
解决办法如下:
const toggle = {
data () {
isshowing: false
},
methods: {
toggleShow() {
this.isshowing = !this.isshowing
}
}
}
// 下面即可使用了
// mixins: [变量名]
const Modal = {
template: '#modal',
mixins: [toggle],
components: {
appChild: Child
}
}
const Tooltip = {
template: '#tooltip',
mixins: [toggle],
components: {
appChild: Child
}
}
选项合并
当组件和混入对象含有同名选项时,这些选项会以恰当的方式混合。 比如数据对象在内部会进行浅合并(一层属性的深度),在和组件的数据发生冲突时,以组件数据优先。
var mixin = {
data() {
return {
msg_mixins: 'mixins',
msg: '123'
}
}
}
var app = new Vue({
el: '#app',
mixins: [mixin],
data: {
msg: 'app'
},
created: function ( ) {
console.log(this.$data)
// => { "msg": "app", "msg_mixins": "mixins" }
}
})
同名的钩子函数将混合为一个数组,所以都会被调用,另外,混入对象的钩子函数会在组件自身的钩子函数之前先进行调用。
var mixin = {
data() {
return {
msg_mixins: 'mixins',
msg: '123'
}
},
created: function ( ) {
console.log('混入对象的钩子被调用')
}
}
var app = new Vue({
mixins: [mixin],
el: '#app',
data: {
msg: 'app'
},
created: function ( ) {
console.log('组件钩子被调用')
}
})
混入对象的钩子被调用
组件钩子被调用
vue.js:9058 You are running Vue in development mode.
Make sure to turn on production mode when deploying for production.
See more tips at https://vuejs.org/guide/deployment.html
当混合值为对象的选项,例如 methods,components,directive
,将被混合为同一个对象,两个对象键名冲突时,取组件的。
混入对象的钩子被调用
merge3.html:47 组件钩子被调用
vue.js:9058 You are running Vue in development mode.
Make sure to turn on production mode when deploying for production.
See more tips at https://vuejs.org/guide/deployment.html
app.foo
ƒ () {
console.log('foo')
}
app.foo
ƒ () {
console.log('foo')
}
app.conflicting
ƒ () {
console.log('from self')
}
注意:Vue.extend() (扩展)
也使用同样的策略进行合并。
全局混入
我们也可以进行全局注册混入对象,注意使用。一旦使用全局混入对象,将会影响所有你创建的 vue 实例,如果使用恰当可以对自定义的对象注入逻辑。比如全局的判断等,会方便一些,不需要每个页面判断,直接取值就行了。
Vue.mixin({
created: function ( ) {
console.log('全局混入')
}
})
var mixin = {
data() {
return {
msg_mixins: 'mixins',
msg: '123'
}
},
created: function ( ) {
console.log('混入对象的钩子被调用')
},
methods: {
foo: function ( ) {
console.log('foo')
},
conflicting: function ( ) {
console.log('from mixin')
}
}
}
var app = new Vue({
mixins: [mixin],
el: '#app',
data: {
msg: 'app'
},
created: function ( ) {
console.log('组件钩子被调用')
},
methods: {
bar: function ( ) {
console.log('bar')
},
conflicting: function ( ) {
console.log('from self')
}
}
})
全局混入
mixin.html:34 混入对象的钩子被调用
mixin.html:53 组件钩子被调用
vue.js:9058 You are running Vue in development mode.
Make sure to turn on production mode when deploying for production.
See more tips at https://vuejs.org/guide/deployment.html
谨慎使用全局混入对象,因为会影响到每个单独创建的 Vue 实例 (包括第三方模板)。大多数情况下,只应当应用于自定义选项。也可以将其用作 Plugins 以避免产生重复应用。
自定义选项合并策略
自定义选项使用默认策略,就是简单的覆盖默认值。如果想让自定义选项以自定义的逻辑合并, 可以向 Vue.config.optionMergeStrategies
添加一个函数:
Vue.config.optionMergeStrategies.myOption = function (toVal, fromVal) {
// 返回合并后的值
}
也就是说,本来的选项的合并方式是默认的,但是我们可以通过修改添加一个函数,来修改合并的方式逻辑。
对于大多数的合并策略来说,可以使用 methods:
var strategies = Vue.config.optionMergeStrategies
strategies.myOption = strategies.methods
可以在 Vuex 1.x 的混入策略里找到一个更高级的例子:
const merge = Vue.config.optionMergeStrategies.computed
Vue.config.optionMergeStrategies.vuex = function (toVal, fromVal) {
if (!toVal) return fromVal
if (!fromVal) return toVal
return {
getters: merge(toVal.getters, fromVal.getters),
state: merge(toVal.state, fromVal.state),
actions: merge(toVal.actions, fromVal.actions)
}
}
extend
在vue3的官方文档中,是这样说的:
从实现的角度看,extends
几乎等同于 mixins
。可以认为其作为第一个 mixin 作用在被 extends
的组件上。
然而,extends
和 mixins
表达了不同的意图。mixins
选项主要用来组合功能,而 extends
主要用来考虑继承性。
和 mixins
类似,任何选项都会通过对应的合并策略被合并
<div id="mount-point"></div> // 创建构造器 var Profile = Vue.extend({ template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>', data: function () { return { firstName: 'Walter', lastName: 'White', alias: 'Heisenberg' } } }) // 创建 Profile 实例,并挂载到一个元素上。 new Profile().$mount('#mount-point')