vue源码剖析-插件入侵机制

插件:智慧的程序员每每希望能更高(tou)效(lan)的完成指定的事情,插件就是依据一定的封装体式格局,暴露接口。让我们应用这些接口更快速的完胜利用。升职加薪。每一个框架都供应了插件的扩大机制。这是框架可扩大性必不可少的一个部份。插件机制越简朴。关于框架的生态的生长大有优点。jquery供应了$.fn.extend,angular有对应的依靠注入,module机制。既然vue那末优美,能敏捷火起来。插件这部份的可扩大性必需顶级。这里接下来我们看看vue插件的入侵机制。
说到插件。我们最多运用的一个要领。不过就是 Vue.use(MyPlugin, { someOption: true });
这么说的话,这个要领应该是一切插件入侵vue的出发点。没错。那末我们来看看这个要领:

Vue.use = function (plugin) {
    /* istanbul ignore if */
    if (plugin.installed) {
      return//如果插件已初始化过就不再继续。防止插件反复入侵
    }
    // additional parameters
    var args = toArray(arguments, 1);//猎取插件的设置参数
    args.unshift(this);
    if (typeof plugin.install === 'function') {
      plugin.install.apply(plugin, args);//挪用的是插件的install要领;
    } else if (typeof plugin === 'function') {
      plugin.apply(null, args);//若插件本省就是一个函数。则直接挪用该函数
    }
    plugin.installed = true;
    return this
  };

Vue.use这个要领让我们晓得来。插件入侵的出发点是挪用插件本身的install函数。那末差别的插件入侵的机制有些时刻很不一样。我们能够晓得。这个不一样一定发生在install函数中。我们来看看官方的install函数中的一些体式格局:

MyPlugin.install = function (Vue, options) {
  // 1. 增加全局要领或属性
  Vue.myGlobalMethod = function () {
    // 逻辑...
  }
  // 2. 增加全局资本
  Vue.directive('my-directive', {
    bind (el, binding, vnode, oldVnode) {
      // 逻辑...
    }
    ...
  })
  // 3. 注入组件
  Vue.mixin({
    created: function () {
      // 逻辑...
    }
    ...
  })
  // 4. 增加实例要领
  Vue.prototype.$myMethod = function (options) {
    // 逻辑...
  }
}

我们按官网引荐的四种例子。来看看每种要领对应的源码:

1:Vue.myGlobalMethod = function () {
    // 逻辑...
  }

相似jquery中的jquery.myGlobalMethod或则$.myGlobalMethod简朴来讲就是给Vue这个全局对象增加一些东西要领。能够供全局快速挪用。我们这里就略过了

2: // 2. 增加全局资本
  Vue.directive('my-directive', {
    bind (el, binding, vnode, oldVnode) {
      // 逻辑...
    }
    ...
  })

Vue.directive,Vue.filter,Vue.component等价。当全局运用这些api时。会在vue上把这些指令过滤器组件等放在响应的属性数组里。形如:

Vue.options = {
    components: {
      
    },
    directives: {},
    filters: {},
    _base: Vue
}

由于他挂在全局的vue中。在vue初始化。挪用init要领时。会实行:

 vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),//战略兼并中心函数。能够细致去看看
        options || {},
        vm
      );

vue在建立实例时。会把vue对象上的options的对象中的属性提取出来和传入的options做兼并。这里涉及到兼并战略。以后会特地讲一下。这里只需晓得。vue每一个设置相都有本身的兼并划定规矩。mergeOptions会依据兼并的类目去挑选对应的兼并划定规矩。这里的component.directive.filter依据兼并划定规矩。Vue对象上的全局的这些属性会被放在实例的__proto__上。
一样的。响应的子组件。能够回过甚去看一下组件那一章。在render建立子组件的时刻。代码以下:

function createComponent (
  Ctor,
  data,
  context,
  children,
  tag
) {
  if (isUndef(Ctor)) {
    return
  }

  var baseCtor = context.$options._base;

  // plain options object: turn it into a constructor
  if (isObject(Ctor)) {
    Ctor = baseCtor.extend(Ctor);
  }

  // if at this stage it's not a constructor or an async component factory,
  // reject.
  if (typeof Ctor !== 'function') {
    {
      warn(("Invalid Component definition: " + (String(Ctor))), context);
    }
    return
  }

  // async component
  if (isUndef(Ctor.cid)) {
    Ctor = resolveAsyncComponent(Ctor, baseCtor, context);
    if (Ctor === undefined) {
      // return nothing if this is indeed an async component
      // wait for the callback to trigger parent update.
      return
    }
  }

  // resolve constructor options in case global mixins are applied after
  // component constructor creation
  resolveConstructorOptions(Ctor);//中心:这里会再次兼并一下vue上的全局的一些指令或则组件或则过滤器到组件的组织函数上

  data = data || {};

  // transform component v-model data into props & events
  if (isDef(data.model)) {
    transformModel(Ctor.options, data);
  }

  // extract props
  var propsData = extractPropsFromVNodeData(data, Ctor, tag);

  // functional component
  if (isTrue(Ctor.options.functional)) {
    return createFunctionalComponent(Ctor, propsData, data, context, children)
  }

  // extract listeners, since these needs to be treated as
  // child component listeners instead of DOM listeners
  var listeners = data.on;
  // replace with listeners with .native modifier
  data.on = data.nativeOn;

  if (isTrue(Ctor.options.abstract)) {
    // abstract components do not keep anything
    // other than props & listeners
    data = {};
  }

  // merge component management hooks onto the placeholder node
  mergeHooks(data);

  // return a placeholder vnode
  var name = Ctor.options.name || tag;
  var vnode = new VNode(
    ("vue-component-" + (Ctor.cid) + (name ? ("-" + name) : '')),
    data, undefined, undefined, undefined, context,
    { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children }
  );
  return vnode
}

末了在内部组件初始化时。vue会挪用

function initInternalComponent (vm, options) {
  var opts = vm.$options = Object.create(vm.constructor.options);//这里照样把组织函数的options放在了$options上供后续运用
  // doing this because it's faster than dynamic enumeration.
  opts.parent = options.parent;
  opts.propsData = options.propsData;
  opts._parentVnode = options._parentVnode;
  opts._parentListeners = options._parentListeners;
  opts._renderChildren = options._renderChildren;
  opts._componentTag = options._componentTag;
  opts._parentElm = options._parentElm;
  opts._refElm = options._refElm;
  if (options.render) {
    opts.render = options.render;
    opts.staticRenderFns = options.staticRenderFns;
  }
}

总的来讲。如果是全局的指令过滤器时。vue一致把他放在根组织要领上。根实例初始化时。经由过程战略兼并兼并到$options中。而子组件轻微绕了一下。终究也是放在$options的原型上。很连接啊。如许只需是全局的组件。指令过滤器。每一个子组件都能够继续运用。达到了插件的结果。

3:下面来看看mixin要领:

Vue.mixin({
    created: function () {
      // 逻辑...
    }
    ...
  })


Vue.mixin = function (mixin) {
    this.options = mergeOptions(this.options, mixin);
  };


//这里照样不可防止要看看mergeOptions函数:
function mergeOptions (
  parent,
  child,
  vm
) {
  {
    checkComponents(child);
  }

  if (typeof child === 'function') {
    child = child.options;
  }

  normalizeProps(child);
  normalizeDirectives(child);
  var extendsFrom = child.extends;
  if (extendsFrom) {
    parent = mergeOptions(parent, extendsFrom, 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
}

分两种状况吧:

a:全局注册时即vue.mixin时。直接挪用兼并。直接方便mixin中的项目。离别挪用响应兼并战略。兼并到组织函数的options中。影响背面一切的子组件

b:部分注册时。

if (child.mixins) {
    for (var i = 0, l = child.mixins.length; i < l; i++) {
      parent = mergeOptions(parent, child.mixins[i], vm);
    }
  }

则会去递归的挪用兼并战略把该兼并的项目兼并完毕为止;

vue.mixin就相称因而一个传入的分外的设置项目,会让vue从新依据划定规矩兼并一次,胜利入侵vue

4:

// 4. 增加实例要领
  Vue.prototype.$myMethod = function (options) {
    // 逻辑...
  }

这个要领就很显著了。在vue的原型上挂载要领。vue的实例自然而然就可以继续。子组件在建立的时刻。

Sub.prototype = Object.create(Super.prototype);
    Sub.prototype.constructor = Sub;

将原型指向根组织函数Vue的prototype;自然而然就会有Vue的原型上的一切属性和要领。。

以上就是vue比较经常使用的插件侵入要领。哈哈。下次再说。告别

    原文作者:fengruiabc
    原文地址: https://segmentfault.com/a/1190000009811112
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞