资讯专栏INFORMATION COLUMN

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

Mike617 / 2119人阅读

摘要:代码如下核心这里会再次合并一下上的全局的一些指令或则组件或则过滤器到组件的构造函数上最后在内部组件初始化时。会调用这里还是把构造函数的放在了上供后续使用总的来说。将原型指向根构造函数的自然而然就会有的原型上的所有属性和方法。。

插件:聪明的程序员往往希望能更高(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比较常用的插件侵入方法。哈哈。下次再说。告辞

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/83494.html

相关文章

  • 读 VuePress(四)插件机制的设计

    前言 从 9 月份开始,vuepress 源码进行了重新设计和拆分。先是开了个 next 分支,后来又合并到 master 分支,为即将发布的 1.x 版本做准备。 最主要的变化是:大部分的全局功能都被拆分成了插件的形式,以可插拔的方式来支撑 vuepress 的运作,这一点很像 webpack。 具体架构如下: showImg(https://user-gold-cdn.xitu.io/2019...

    April 评论0 收藏0
  • Vuex源码解析

    摘要:可能会有理解存在偏差的地方,欢迎提指出,共同学习,共同进步。先来看一下这张的数据流程图,熟悉使用的同学应该已经有所了解。它允许用户在某些情况下避免自动安装。 写在前面 因为对Vue.js很感兴趣,而且平时工作的技术栈也是Vue.js,这几个月花了些时间研究学习了一下Vue.js源码,并做了总结与输出。 文章的原地址:https://github.com/answershuto/lear...

    chinafgj 评论0 收藏0
  • 前端窝 - 收藏集 - 掘金

    摘要:毫无疑问,设计模式于己于他人于系统都是多赢的设计模式使代码编写真正工程化设计模小书前端掘金这是一本关于的小书。 JavaScript 常见设计模式解析 - 掘金设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。毫无疑问,设计模式于己于他人于系统都是多赢的;设计...

    李文鹏 评论0 收藏0
  • 深入解析Vue源码

    摘要:你可以使用的方法传入指令和定义对象来注册一个全局自定义指令。深度数据观察如果你希望在一个对象上使用自定义指令,并且当对象内部嵌套的属性发生变化时也能够触发指令的函数,那么你就要在指令的定义中传入。 Vue简介 数据绑定 /** *假设有这么两个钟东西 **/ //数据 var object = { message: Hello World! } //DOM {{ messag...

    weapon 评论0 收藏0
  • [全网最全的 Vue CLI 3 原创合集] 你要的这里都有

    摘要:慢慢地,关于的原创学习文章已经写了多篇了会一直放出来,目前篇,因此做一个合集,献给那些对新版本脚手架使用和背后设计感兴趣的同学,都是一步一步去看源码,也给官方提了几次,合进去了几个原创不易,欢迎大家互相转发,期望大家一起快速过度到版本目录 慢慢地,关于 Vue CLI 3 的原创学习文章已经写了 20 多篇了(会一直放出来,目前 23 篇), 因此做一个合集,献给那些对新版本脚手架使用...

    phpmatt 评论0 收藏0

发表评论

0条评论

Mike617

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<