# Vue.mixin/extend

在项目中, 使用import Vue from 'vue'来引入vue的构造函数.

Vue.mixin()则会不断地修改Vue.options从而永久性地影响之后实例化的对象

Vue.mixin = function (mixin: Object) {
  this.options = mergeOptions(this.options, mixin)
  return this
}
1
2
3
4

我们同样可以利用Vue.extend创建一个子类, 为它预置一些options, 但是它不会修改Vue.options, 因此不会影响父类. 你可以构造你的子类, 限制影响范围.

TIP

但是Vue.mixin会影响到所有Vue.extend的子类. 即便你是如下的顺序去创建子类, 最后调用的Vue.mixin也会影响到子类. vue内部有专门的代码判断这种情况 (opens new window)

Vue.mixin(option1)
const Child = Vue.extend(optionChild)
Vue.mixin(option2)
1
2
3

Vue.mixin通常只有在你需要全局地影响你项目中所有的Vue实例, 才会用到.

比如一些插件vue-router, vuex, vue-router需要全局性地注入一些生命钩子, 从而保证你所有的组件都能通过this.$router来访问到router实例, ect.

# 为什么突然注意到Vue.mixin?

我们使用Vue.ues来注册插件, 配合Vue.component可以全局注册组件, Vue.component实际上是在Vue的构造函数上添加这些组件(vm.constructor.options.components), 从而使得后续实例化的Vue实例, 全都能从vm.$options上拿到这些组件(通过将vm.$options指向vm.constructor.options)

vue/src/core/instance/init.js

  const opts = vm.$options = Object.create(vm.constructor.options)
1

但是在一次业务开发中, 我发现vm.constructor.options.components的结构并不是很直观, 原型链有很多冗余

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false

Vue.component('globalComponentExample', {
  data () {
    return {
    }
  }
})

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

console.log(Vue.options)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

对应的Vue.options如下

截屏2020-09-13 下午9.08.23

如果代码改成这样


Vue.config.productionTip = false

Vue.mixin({})
Vue.mixin({})

Vue.component('globalComponentExample', {
  data () {
    return {
    }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12

components的原型链会变成这样 2020-09-13-21-40-12

这个问题的根本是涉及了Vue.mixincomponents的合并策略

  Vue.mixin = function (mixin: Object) {
    this.options = mergeOptions(this.options, mixin)
    return this
  }
1
2
3
4

Vue.mixin时, 会先针对Vue.options上已有的属性进行一次合并

mergeOptions

 
 
 






  for (key in parent) {
    mergeField(key)
  }
  for (key in child) {
    if (!hasOwn(parent, key)) {
      mergeField(key)
    }
  }
1
2
3
4
5
6
7
8

Vue.options上默认肯定是有components属性的, 所以我们使用Vue.mixin的时候, 肯定会执行一次mergeField('components'), 而components的合并策略由下面的代码决定. 所以每调用一次Vue.mixin, components的原型链长度就会+1.

components的这种合并策略, 持保持态度, 没什么大问题







 












function mergeAssets (
  parentVal: ?Object,
  childVal: ?Object,
  vm?: Component,
  key: string
): Object {
  const res = Object.create(parentVal || null)
  if (childVal) {
    process.env.NODE_ENV !== 'production' && assertObjectType(key, childVal, vm)
    return extend(res, childVal)
  } else {
    return res
  }
}

ASSET_TYPES.forEach(function (type) {
  strats[type + 's'] = mergeAssets
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

实际上, 很多地方都会有Vue.mixin的调用, 比如vue router, vuex, vue devtool, 所以有时候Vue.options.components会呈现一种原型链冗长的状态.