[vue源码笔记06]vue2.x的全局方法

174 阅读3分钟

Vue.use

作用:插件安装

源代码:

Vue.use = function (plugin: Function | Object) {
  const installedPlugins =
    this._installedPlugins || (this._installedPlugins = []); // 所有的插件都保存在Vue._installedPlugins队列里
  if (installedPlugins.indexOf(plugin) > -1) { // 避免重复安装
    return this;
  }
  // 从第2个参数开始作为传入插件的参数
  const args = toArray(arguments, 1);
  args.unshift(this); // 将Vue构造函数作为第一个参数传入
  // 如果插件实现了install函数,则调用
  if (typeof plugin.install === "function") {
    plugin.install.apply(plugin, args);
  }
  // 如果插件本事是一个函数,则调用
  else if (typeof plugin === "function") {
    plugin.apply(null, args);
  }
  installedPlugins.push(plugin);
  return this;
};

解释: 详细解释见代码注释,从源码可以看出Vue维护了一个队列用于保存所有安装过的插件,插件的安装实际上是执行了插件的install方法(前提是插件实现了该方法)或者直接执行插件函数,其将接收一系列参数,其中第一个参数为Vue构造函数

Vue.mixin

作用:合并传入options和Vue.options

源代码:

function mergeOptions (
  parent: Object,
  child: Object,
  vm?: Component
): Object {
  if (typeof child === 'function') {
    child = child.options
  }

  normalizeProps(child, vm) // 标准化被合并props最终将输出{key: {type: String, default: ''}}类型
  normalizeInject(child, vm) // 标准化inject
  normalizeDirectives(child) // 标准化directives
  const extendsFrom = child.extends
  if (extendsFrom) { // 单独对child.extends进行合并
    parent = mergeOptions(parent, extendsFrom, vm)
  }
  if (child.mixins) { // 如果child定义了mixins,则首先将其合并到parent,目的是降低其优先级
    for (let i = 0, l = child.mixins.length; i < l; i++) {
      parent = mergeOptions(parent, child.mixins[i], vm)
    }
  }
  const options = {}
  let key
  for (key in parent) {
    mergeField(key)
  }
  for (key in child) {
    if (!hasOwn(parent, key)) {
      mergeField(key)
    }
  }
  // 针对不同类型的option调用不同的合并策略方法进行合并
  function mergeField (key) {
    const strat = strats[key] || defaultStrat
    options[key] = strat(parent[key], child[key], vm, key)
  }
  return options
}
Vue.mixin = function (mixin: Object) {
  this.options = mergeOptions(this.options, mixin);
  return this;
};

解释:Vue.mixin的作用就是将传入optionVue.options做一次合并,对Vue.options进行拓展,具体的合并过程见代码注释

至于注释中提到的针对不同类型的option调用不同的合并策略方法,下面介绍几种合并策略:

// 使用源对象的属性覆盖目标对象的属性
function extend (to: Object, _from: ?Object): Object {
  for (const key in _from) {
    to[key] = _from[key]
  }
  return to
}

// 资源合并:源对象作为目标对象的原型,不对目标对象做覆盖
// 使用该策略合并的option有:components、directives、filters
function mergeAssets (
  parentVal: ?Object,
  childVal: ?Object,
  vm?: Component,
  key: string
): Object {
  const res = Object.create(parentVal || null)
  if (childVal) {
    return extend(res, childVal)
  } else {
    return res
  }
}

// 默认合并:如果目标对象为undefined则直接使用源对象,否则使用目标对象
// 使用该策略合并的option有:_base
function defaultStrat (parentVal: any, childVal: any): any {
  return childVal === undefined
    ? parentVal
    : childVal
}

// 覆盖合并:目标对象属性将覆盖源对象的属性
// 使用该策略的option有:props、methods、inject、computed
function merge (
  parentVal: ?Object,
  childVal: ?Object,
  vm?: Component,
  key: string
): ?Object {
  if (!parentVal) return childVal
  const ret = Object.create(null)
  extend(ret, parentVal)
  if (childVal) extend(ret, childVal)
  return ret
}
... 其他合并策略不多做介绍

扩展:Vue.options的用途

在创建Vue实例的过程中首先需要将用户传入options和Vue.options进行一次合并,即全局定义的Vue.options将在每个实例中都能进行访问

// 这里的mergeOPtions方法上面已经有过介绍
vm.$options = mergeOptions(
    vm.constructor.options, // 源码中并不简单是这样,这里便于理解
    options || {},
    vm
)

Vue.extend

作用:创建vue组件构造器

源代码:

Vue.extend = function (extendOptions: Object): Function { // 参数为用户自定义组件options
  extendOptions = extendOptions || {};
  const Super = this; // this指向Vue
  const SuperId = Super.cid; // Vue的唯一标识
  const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {}); // 缓存
  // 优先读取缓存,如果有缓存,直接返回缓存
  if (cachedCtors[SuperId]) {
    return cachedCtors[SuperId];
  }

  // name: 组件名称
  const name = extendOptions.name || Super.options.name;

  // Sub继承自Vue(super)
  const Sub = function VueComponent(options) {
    this._init(options); // this._init继承自Vue
  };
  Sub.prototype = Object.create(Super.prototype);
  Sub.prototype.constructor = Sub;
  Sub.cid = cid++;
  // 合并Vue.options和component,该方法同Vue.mixin中调用的合并方法,具体参看Vue.mixin小节
  Sub.options = mergeOptions(Super.options, extendOptions);
  Sub["super"] = Super;

  // 这里主要对props、computed做了一层代理Sub.key --> Sub._props.key
  if (Sub.options.props) {
    initProps(Sub);
  }
  if (Sub.options.computed) {
    initComputed(Sub);
  }

  // 拷贝Vue的静态方法给Sub
  Sub.extend = Super.extend;
  Sub.mixin = Super.mixin;
  Sub.use = Super.use;

  // Sub.component = Vue.component Sub.directive = Vue.directive Sub.filter = Vue.filter
  ASSET_TYPES.forEach(function (type) {
    Sub[type] = Super[type];
  });
  // 保证自身循环查找
  if (name) {
    Sub.options.components[name] = Sub;
  }

  // 保持对Vue.options 和 用户传入options的引用
  Sub.superOptions = Super.options;
  Sub.extendOptions = extendOptions;
  Sub.sealedOptions = extend({}, Sub.options);

  cachedCtors[SuperId] = Sub; // 缓存
  // 返回值是一个构造函数
  return Sub;
};

解释: 具体见代码注释

Vue.component & Vue.directive & Vue.filter

说明:这三个静态方法放在一起,在Vue中全局component、directive、filter都称为assets,他们公用一个方法

源代码:

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

ASSET_TYPES.forEach((type) => {
  Vue[type] = function (
    id: string,
    definition: Function | Object
  ): Function | Object | void {
    if (!definition) {
      return this.options[type + "s"][id]; // 如果没有传入options则直接返回Vue.options.components[id]
    } else {
      if (type === "component" && isPlainObject(definition)) {
        definition.name = definition.name || id;
        definition = this.options._base.extend(definition); // 对于component调用Vue.extend创建组件构造器
      }
      if (type === "directive" && typeof definition === "function") {
        definition = { bind: definition, update: definition };
      }
      this.options[type + "s"][id] = definition; // 将新增的definition保存在全局
      return definition;
    }
  };
});

其它

Vue.set === Vue.prototype.$set

Vue.delete === Vue.prototype.$delete

Vue.nextTick === Vue.prototype.$nextTick