zoukankan      html  css  js  c++  java
  • vue 合并配置mixin

    new Vue 的过程通常有 2 种场景,一种是外部我们的代码主动调用 new Vue(options) 的方式实例化一个 Vue 对象;另一种是组件过程中内部通过 new Vue(options) 实例化子组件。

    无论哪种场景,都会执行实例的 _init(options) 方法,它首先会执行一个 merge options 的逻辑

    可以看到不同场景对于 options 的合并逻辑是不一样的,并且传入的 options 值也有非常大的不同

     Vue.prototype._init = function (options) {
          var vm = this;
          // a uid
          vm._uid = uid$3++;
    
          var startTag, endTag;
          /* istanbul ignore if */
          if (config.performance && mark) {
            startTag = "vue-perf-start:" + (vm._uid);
            endTag = "vue-perf-end:" + (vm._uid);
            mark(startTag);
          }
    
          // a flag to avoid this being observed
          vm._isVue = true;
          // merge options
          if (options && options._isComponent) {
            // optimize internal component instantiation
            // since dynamic options merging is pretty slow, and none of the
            // internal component options needs special treatment.
            initInternalComponent(vm, options);
          } else {
    //
    外部调用场景:当执行 new Vue 的时候,在执行 this._init(options) 的时候,就会执行如下逻辑去合并 options

    它实际上就是把 resolveConstructorOptions(vm.constructor) 的返回值和 options 做合并,resolveConstructorOptions 的实现先不考虑,

    在我们这个场景下,它还是简单返回 vm.constructor.options,相当于 Vue.options,那么这个值又是什么呢,其实在 initGlobalAPI(Vue) 的时候定义了这个值

            vm.$options = mergeOptions(
              resolveConstructorOptions(vm.constructor),
              options || {},
              vm
            );
          }
          /* istanbul ignore else */
          {
            initProxy(vm);
          }

    首先通过 Vue.options = Object.create(null) 创建一个空对象,然后遍历 ASSET_TYPES

    export const ASSET_TYPES = [
      'component',
      'directive',
      'filter'
    ]

    所以上面遍历 ASSET_TYPES 后的代码相当于

    Vue.options.components = {}
    Vue.options.directives = {}
    Vue.options.filters = {}

    接着执行了 Vue.options._base = Vue

    最后通过 extend(Vue.options.components, builtInComponents) 把一些内置组件扩展到 Vue.options.components 上,

    Vue 的内置组件目前有 <keep-alive><transition> 和 <transition-group> 组件,这也就是为什么我们在其它组件中使用 <keep-alive> 组件不需要注册的原因


    export function initGlobalAPI (Vue: GlobalAPI) { // ... Vue.options = Object.create(null) ASSET_TYPES.forEach(type => { Vue.options[type + 's'] = Object.create(null) }) // this is used to identify the "base" constructor to extend all plain-object // components with in Weex's multi-instance scenarios. Vue.options._base = Vue extend(Vue.options.components, builtInComponents) // ... }
      /**
       * Merge two option objects into a new one.
       * Core utility used in both instantiation and inheritance.
       */

    mergeOptions 主要功能就是把 parent 和 child 这两个对象根据一些合并策略,合并成一个新对象并返回。比较核心的几步,

    先递归把 extends 和 mixixns 合并到 parent 上,然后遍历 parent,调用 mergeField,然后再遍历 child,如果 key 不在 perent 的自身属性上,则调用 mergeField

    这里有意思的是 mergeField 函数,它对不同的 key 有着不同的合并策略。

      function mergeOptions (
        parent,
        child,
        vm
      ) {
        {
          checkComponents(child);
        }
    
        if (typeof child === 'function') {
          child = child.options;
        }
    
        normalizeProps(child, vm);
        normalizeInject(child, vm);
        normalizeDirectives(child);
    
        // Apply extends and mixins on the child options,
        // but only if it is a raw options object that isn't
        // the result of another mergeOptions call.
        // Only merged options has the _base property.
        if (!child._base) {
          if (child.extends) {
            parent = mergeOptions(parent, child.extends, vm);
          }
          if (child.mixins) {
            for (var i = 0, l = child.mixins.length; i < l; i++) {
              parent = mergeOptions(parent, child.mixins[i], vm);
            }
          }
        }
    
        var options = {};
        var key;
        for (key in parent) {
          mergeField(key);
        }
        for (key in child) {
          if (!hasOwn(parent, key)) {
            mergeField(key);
          }
        }
        function mergeField (key) {
          var strat = strats[key] || defaultStrat;
          options[key] = strat(parent[key], child[key], vm, key);
        }
        return options
      }
    
    

    这里定义了 Vue.js 所有的钩子函数名称,所以对于钩子函数,他们的合并策略都是 mergeHook 函数。这个函数的实现也非常有意思,

    用了一个多层 3 元运算符,逻辑就是如果不存在 childVal ,就返回 parentVal;否则再判断是否存在 parentVal,如果存在就把 childVal 添加到 parentVal 后返回新数组;

    否则返回 childVal 的数组。所以回到 mergeOptions 函数,一旦 parent 和 child 都定义了相同的钩子函数,那么它们会把 2 个钩子函数合并成一个数组。

    function mergeHook (
      parentVal: ?Array<Function>,
      childVal: ?Function | ?Array<Function>
    ): ?Array<Function> {
      return childVal
        ? parentVal
          ? parentVal.concat(childVal)
          : Array.isArray(childVal)
            ? childVal
            : [childVal]
        : parentVal
    }
    
    LIFECYCLE_HOOKS.forEach(hook => {
      strats[hook] = mergeHook
    })
    export const LIFECYCLE_HOOKS
    = [ 'beforeCreate', 'created', 'beforeMount', 'mounted', 'beforeUpdate', 'updated', 'beforeDestroy', 'destroyed', 'activated', 'deactivated', 'errorCaptured' ]

    vm.$options 的值差不多是如下这样

    vm.$options = {
      components: { },
      created: [
        function created() {
          console.log('parent created') 
        }
      ],
      directives: { },
      filters: { },
      _base: function Vue(options) {
        // ...
      },
      el: "#app",
      render: function (h) {  
        //...
      }
    }
  • 相关阅读:
    POJ 1236 Network of Schools(tarjan算法 + LCA)
    Hrbustoj 2266 Legendary Weights(辗转相除求最大公约数)
    纯虚函数的学习和使用
    完全背包(内含不能恰好装满的情况)
    多重背包并判断能否装满(附01完全背包思想)
    UVA 796 Critical Links (tarjan算法求割边)
    poj 2594 Treasure Exploration(最小路径覆盖,可重点)
    poj 3020 Antenna Placement (最小路径覆盖)
    ZOJ 1642
    Playground
  • 原文地址:https://www.cnblogs.com/TTblog5/p/12866836.html
Copyright © 2011-2022 走看看