Skip to content

mixin

javascript
// 全局混入, 影响之后创建的每个Vue实例
Vue.mixin({
  beforeCreate: function () {
    console.log('beforeCreate')
  }
})

Vue.mixin({ 
  beforeCreate: [
    function () {}, 
    function () {}
  ] 
})
javascript
// Vue.mixin 中将 mixin 的配置项和全局配置项 Vue.options 进行合并
Vue.mixin = function (mixin) {
  // 合并配置项
  this.options = mergeOptions(this.options, mixin)
  return this
}
javascript
// mergeOptions 用于合并父子配置项,内部定义一个对象 options 用于保存合并结果并最终
// 返回,先遍历 parent,对每个属性调用 mergeField 方法,再遍历 child,对 parent
// 中没有的属性调用 mergeField 方法。
// mergeField 中根据不同的属性选择不同的合并策略,默认的合并策略是只有 childVal
// 为 undefined 时以 parentVal 为准,否则都让 childVal 覆盖 parentVal。
function mergeOptions (parent, child) {
  const options = {}
  let key

  for (key in parent) {
    mergeField(key)
  }
  for (key in child) {
    if (!hasOwn(parent, key)) { // 父子中相同的key合并过了
      mergeField(key)
    }
  }

  function mergeField (key) {
    let strat = strats[key] || defaultStrat // 选择合并策略
    options[key] = strat(parent[key], child[key])
  }
  return options
}

Vue.prototype._init = function (options) {
	// 合并全局options和组件options
	vm.$options = mergeOptions(vm.constructor.options, options || {})
}

// 钩子合并策略
const LIFECYCLE_HOOKS = [
  'beforeCreate',
  'created',
  'beforeMount',
  'mounted',
  'beforeUpdate',
  'updated',
  'beforeDestroy',
  'destroyed',
  'activated',
  'deactivated',
  'errorCaptured',
  'serverPrefetch'
];

const strats = {}
// 默认合并策略 以childVal为准
const defaultStrat = function (parentVal, childVal) {
  return childVal === undefined
    ? parentVal
    : childVal
}

// 钩子合并策略
LIFECYCLE_HOOKS.forEach(hook => {
	strats[hook] = mergeHook
})

function mergeHook (parentVal, childVal) {
  const res = childVal
    ? parentVal
        // 合并Vue.options[hook] 和 vm.$options[hook],确保mixin 混入的先执行,
        ? parentVal.concat(childVal) 
        : Array.isArray(childVal)
          ? childVal
          : [childVal] // 包装成数组
    : parentVal // vm.$options[hook] 可能为 undefined

  return res
}

// 在合适的位置调用即可
function callHook (vm, hook) {
  const handlers = vm.$options[hook]
  if (handlers) {
    for (let i = 0, l = handlers.length; i < l; i++) {
      handlers[i].call(vm)
    }
  }
}