Vue2.x源码解读之Vue.use

1,227 阅读4分钟

相信这个use方法大家肯定不陌生,加载路由插件Vue.use(VueRouter),加载状态管理插件Vue.use(Vuex),加载Element插件Vue.use(Button).use(Input),那它底层源码究竟是怎么实现加载Vue相关的插件和链式调用的,下面一步一步带各位读者"掰开揉碎"实现代码。

Vue.use方法介绍

先上官网对Vue.use的方法介绍:

  1. 安装Vue.js插件。如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将Vue作为参数传入。

  2. 该方法需要在调用 new Vue() 之前被调用。

  3. 当 install 方法被同一个插件多次调用,插件将只会被安装一次。

上面的介绍主要是对use方法的功能和使用的注意提示,这里注意的是use方法传入的参数数量其实是没有限制的,支持多个参数;还有第一个参数可以传入带install属性方法的对象或者是函数。

Vue.use源码分析

直接上Vue.use的源码:

// ...
Vue.use = function (plugin) {
  var installedPlugins = (this._installedPlugins || (this._installedPlugins = []));
  if (installedPlugins.indexOf(plugin) > -1) {
    return this
  }
  // additional parameters
  var args = toArray(arguments, 1);
  args.unshift(this);
  if (typeof plugin.install === 'function') {
    plugin.install.apply(plugin, args);
  } else if (typeof plugin === 'function') {
    plugin.apply(null, args);
  }
  installedPlugins.push(plugin);
  return this
};
// ...

这里分成是4个步骤进行分析:

第一步:只调用一次install

实现的效果就是当install被多次调用的时候,插件只会被安装一次。检查当前的Vue对象里的_installedPlugins数组上是否有当前插件对象,如果存在则直接完成返回当前的Vue对象(也就是代码里面看到的return this

Vue.use = function (plugin) {
  // 第一步:当install被多次调用的时候,插件只会被安装一次。
  var installedPlugins = (this._installedPlugins || (this._installedPlugins = []));
  if (installedPlugins.indexOf(plugin) > -1) {
    return this
  }
  // ....
}

第二步:把arguments转换成数组

处理参数,把arguments上的其余参数(排除第一个)转换成一个新的数组,把当前的Vue对象(也就是代码中的this)作为新数组的第一个参数

// 将类数组对象转换成数组,start就是转换的起始位置
function toArray (list, start) {
  start = start || 0;
  var i = list.length - start;
  var ret = new Array(i);
  while (i--) {
    ret[i] = list[i + start];
  }
  return ret
}

Vue.use = function (plugin) {
  // ...
  
  /**
   * 第二步:
   * arguments删除第一个参数后转换成真正的数组,并且添加this作为数组的第一个元素
   * 例如:Vue.use(MyTest, 1, 2, 3); 经过处理后新的数组:args = [this, 1, 2, 3]
  */
  var args = toArray(arguments, 1);
  // 往新数组最前面添加一个this
  args.unshift(this);
}

第三步:调用插件的install方法

判断use方法的第一个参数的install属性是否为函数,又或者第一个参数是否为函数,如果有一个满足则执行apply方法调用第一个参数函数,并且把use方法剩余的参数传入。

Vue.use = function (plugin) {
  // ...
  /**
   * 第三步:
	 * 假设现在:Vue.use(MyTest, 1, 2, 3);
	 * 1. 第一个判断表达式中的plugin就是MyTest,也就是判断MyTest上的install属性是否为函数,如果是则使用apply方法调用MyTest.install函数,并且把this绑定在MyTest上。
	 * 2. 第二个判断表达式中判断plugin(MyTest)是否为函数,如果是则传入剩余参数使用apply直接执行调用。
   */
  if (typeof plugin.install === 'function') {
    plugin.install.apply(plugin, args);
    // 另外一种Vue.use的第一参数是函数,那么就直接数组作为参数传入,例如:Mytest(this, 1, 2, 3, 4, 5),这里的this其实就是Vue
  } else if (typeof plugin === 'function') {
    plugin.apply(null, args);
  }
}

第四步:返回this

_installedPlugins数组末尾添加当前插件对象,然后返回this,也就是当前的Vue对象。这里返回this就是为了能让use方法能够支持链式调用。

// 将类数组对象转换成数组,start就是转换的起始位置
function toArray (list, start) {
  start = start || 0;
  var i = list.length - start;
  var ret = new Array(i);
  while (i--) {
    ret[i] = list[i + start];
  }
  return ret
}

Vue.use = function (plugin) {
  // ...
  // 第四步:添加插件plugin到this._installedPlugins数组末尾,返回this
  installedPlugins.push(plugin);
  // 这个返回this,能够实现链式调用
  return this
}

如果读者发现有不妥或者可以改善的地方,欢迎在评论区指出。如果觉得写得不错或者对你有所帮助,可以点赞、评论、转发分享,谢谢~