选项合并策略剖析(2)

[选项合并策略剖析(1)]()

生命周期钩子函数合并策略

在 Vue 中,生命周期是一个很重要的知识点,咱们能够在生命周期各个阶段做不同的事件。当初咱们来看一下 Vue 生命周期钩子函数的合并策略

LIFECYCLE_HOOKS.forEach(hook => {  strats[hook] = mergeHook})function mergeHook (  parentVal: ?Array<Function>,  childVal: ?Function | ?Array<Function>): ?Array<Function> {  /**   * 合并策略   * 1. 子类不存在钩子选项时,应用父类钩子选项   * 2. 子类存在钩子选项,父类不存在钩子选项时,应用子类钩子选项   * 3. 子类和父类都存在钩子选项时,进行合并,将子类的钩子选项放在数组开端,在执行钩子函数时,父类的钩子函数优先于子类执行   */  const res = childVal    ? parentVal      ? parentVal.concat(childVal)      : Array.isArray(childVal)        ? childVal        : [childVal]    : parentVal  return res    ? dedupeHooks(res)    : res}function dedupeHooks (hooks) {  const res = []  for (let i = 0; i < hooks.length; i++) {    if (res.indexOf(hooks[i]) === -1) {      res.push(hooks[i])    }  }  return res}

在这里解释一下下面的三元表达式的执行逻辑:

    1. 子类钩子函数不存在时,应用父类的钩子函数
    1. 子类的钩子函数存在父类钩子不存在,应用子类的钩子函数,(如果子类的钩子函数不是数组,则转换成数组)
    1. 子类和父类的钩子函数都存在时,将子类的钩子函数增加到数组开端

总结一下:钩子函数在合并时,子类和父类雷同的选项会合并成数组,数组的每一项都是一个函数 在钩子函数执行时,父类钩子函数也会执行,并且因为子类钩子执行

举例说明

const parent = Vue.extend({  data(){    return {      a: 1    }  },  created(){    console.log("aaaaa")  }})var sub = parent.extend({  // el:"#root",  子类中不容许蕴含 el 选项  data(){    return {      b: 2    }  },  created(){    console.log("bbbbb")  }})var subVm = new sub()console.log(subVm.$data)subVm.$mount("#root")

下面的例子中, parent 实例中的钩子函数 created 会与 Vue 结构器器中的钩子函数(此时为 undefined)进行合并,失去的后果为 [function created]

sub 实例会与 parent 中的钩子函数进行合并,此时 parent 的钩子函数选项为 [function created],合并后 sub 实例的 options 中的 created 钩子函数为 [function created, function created](其中第一个为 parent 中钩子函数,第二个为 sub 本身的钩子函数)。 最终失去的 sub 实例中 options 选项如下图:

watch 选项合并

strats.watch = function (  parentVal: ?Object,  childVal: ?Object,  vm?: Component,  key: string): ?Object {  // work around Firefox's Object.prototype.watch...  // 火狐浏览器在 Object 原型上领有 watch ,进行兼容, export const nativeWatch = ({}).watch  if (parentVal === nativeWatch) parentVal = undefined  if (childVal === nativeWatch) childVal = undefined  /* istanbul ignore if */  if (!childVal) return Object.create(parentVal || null)  if (process.env.NODE_ENV !== 'production') {    // watch 选项必须是对象    assertObjectType(key, childVal, vm)  }  // 父类的 watch 选项不存在,则间接应用子类本身的 watch 选项  if (!parentVal) return childVal  const ret = {}  // 首先将父类的 watch 选项赋值给 ret 对象,而后便当子类的选项,  extend(ret, parentVal)  for (const key in childVal) {    let parent = ret[key]    const child = childVal[key]    // 先将父类 watch 选项中对 [key] 属性监听的 handler 办法转换成数组(如果存在的话)    if (parent && !Array.isArray(parent)) {      parent = [parent]    }    ret[key] = parent      ? parent.concat(child)      : Array.isArray(child) ? child : [child]  }  return ret}

对于 watch 选线的合并策略和生命周期钩子函数合并策略有些相似,只有父类选项中存在雷同的观测字段,则和子类的选项合并成数组。因为 watch 选项有多种写法,合并之后的选项也存在多种状况

  • 合并后的 watch 选项是一个对象,键为监听的属性名,值为回掉函数
  • 合并后的 watch 选项是一个对象数组,数组每一项的键为监听的属性名,值为回掉函数
  • 合并后的 watch 选项是一字符串/字符串数组(字符串为函数名,该办法在 methods 选项中进行定义)

举例

const parent = Vue.extend({  data(){    return {      a: 1    }  },  watch:{    a: ['achange', 'change1']  },  methods:{    achange(){      console.log("achange")    },    change1(){      console.log('aaaaaa')    }  }})var sub = parent.extend({  // el:"#root",  子类中不容许蕴含 el 选项  data(){    return {      b: 2    }  },  watch:{    b: {      handler(newVal, oldVal){        console.log(newVal)      }    },    a:{      handler(newVal, oldVal){        console.log("sub watch a change")        console.log(newVal)      }    }  }})

下面的例子中,最终合并之后, parent 中 watch 选项如下图所示:

sub 中 watch 选项如下图所示

props methods inject computed 合并策略

props、methods、inject、computed 这些选项的数据类型都是对象(props 和 inject 在选项标准话时曾经转换成对象格局),合并策略是雷同的,也绝对比较简单。
如果父类选项不存在,则应用子类选项。如果子类和父类选项同时存在,应用子类选项笼罩父类选项

// 代码地位 core/util/options.jsstrats.props =strats.methods =strats.inject =strats.computed = function (  parentVal: ?Object,  childVal: ?Object,  vm?: Component,  key: string): ?Object {  if (childVal && process.env.NODE_ENV !== 'production') {    assertObjectType(key, childVal, vm)  }  // 父类不存在对应的选项,间接应用子类的选项  if (!parentVal) return childVal  const ret = Object.create(null)  extend(ret, parentVal)  // 父类和子类选项都存在时,应用子类选项笼罩父类选项  if (childVal) extend(ret, childVal)  return ret}

provide 合并策略

provide 的合并策略和 data 合并策略统一,不同点就是 provide 没有规定必须时函数