前言
全局API和实例方法不同,后者是在Vue的原型上挂载方法,也就是在Vue.prototype上挂载方法,而前者是直接在Vue上挂载方法。例如:
Vue.extend = funciton(extendOptions) {
//做点什么
}
Vue.extend
Vue.extend的作用是创建一个子类,所以可以创建一个子类,然后让它继承Vue身上的一些功能。
/**
* 参数
* { Object } extendOptions 组件选项的对象
*/
Vue.cid = 0;
var cid = 1;
Vue.extend = function (extendOptions) {
//传入了参数使用传入的如果没穿使用{}
extendOptions = extendOptions || {};
// 指向父类,即基础 Vue类;
var Super = this;
// 父类的cid属性,无论是基础Vue类还是从基础Vue类继承而来的类,都有一个cid属性,作为该类的唯一标识;
var SuperId = Super.cid;
//如果传入的对象参数不存在_Ctor属性,extend会给他添加上这个属性,用于缓存。
var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {});
//如果存在_Ctor这个属性,并且这个对象上存在SuperId这个键的话就会直接返回缓存过的构造函数
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
//校验name名称是否符合规则
var name = extendOptions.name || Super.options.name;
{
if (!/^[a-zA-Z][\w-]*$/.test(name)) {
warn(
'Invalid component name: "' + name + '". Component names ' +
'can only contain alphanumeric characters and the hyphen, ' +
'and must start with a letter.'
);
}
}
//这就是子类的构造函数
var Sub = function VueComponent (options) {
this._init(options);
};
// 继承vue的原型方法
Sub.prototype = Object.create(Super.prototype);
//把constructor的指向自己
Sub.prototype.constructor = Sub;
//Sub添加一个自己的属性cid用于缓存
Sub.cid = cid++;
//合并配置
Sub.options = mergeOptions(
Super.options,
extendOptions
);
//把父类(也就是vue)挂载到super属性方便以后使用
Sub['super'] = Super;
// //判断是否有props属性如果有就初始化props
if (Sub.options.props) {
initProps$1(Sub);
}
//判断是否有computed属性如果有就初始化computed
if (Sub.options.computed) {
initComputed$1(Sub);
}
//把父类的extend mixin use 方法挂载到Sub上允许他进一步扩展
Sub.extend = Super.extend;
Sub.mixin = Super.mixin;
Sub.use = Super.use;
//ASSET_TYPES是数组['component','directive','filter']
//把super(父类)的component,directive,filter 方法挂载到sub(子类)上
//使sub具有注册组件 ,注册指令,注册自定义过滤器
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type];
});
//name有值的时候将自己存到options.components[name]上
if (name) {
Sub.options.components[name] = Sub;
}
// 将这些options保存起来,一会创建实例的时候会用到.
Sub.superOptions = Super.options;
Sub.extendOptions = extendOptions;
Sub.sealedOptions = extend({}, Sub.options);
// 缓存池,用于缓存创建出来的类
cachedCtors[SuperId] = Sub;
return Sub
};
Vue.directive/Vue.filter/Vue.component
之所以将directive、filter、component三个方法放到一起,是因为这三个方法实现的原理是一模一样的,在vue的源码中,也是一起实现的。
其中,Vue.directive用于注册或获取全局指令。
Vue.filter用于注册或获取全局过滤器。
Vue.component用于注册或获取全局组件。
/**
* 参数
* {string} id
* { Function | Object } [definition]
*/
var ASSET_TYPES = [
'component', // 组件
'directive', // 指令
'filter' // 注册
];
ASSET_TYPES.forEach(function (type) {
Vue[type] = function (id, definition) {
// 如果没有传递 definition 的话,直接返回 id 对应的 definition,即获取指令/过滤器/组件
if (!definition) {
return this.options[type + 's'][id]
} else {
// 下面进行注册操作,其实所谓的注册操作:只是将要注册的东西找个地方存放起来而已。
{
if (type === 'component' && config.isReservedTag(id)) {
// 判断注册组件的 tag 是不是 Vue 内置的组件,或者是 HTML 的保留标签
warn(
'Do not use built-in or reserved HTML elements as component ' +
'id: ' + id
);
}
}
if (type === 'component' && isPlainObject(definition)) {
// 进行组件 name 的处理,如果 definition 中没有 name 的话,则使用 id 作为组件的 name
definition.name = definition.name || id;
//_base 属性其实就是 Vue 构造函数
// 组件的本质就是 Vue 构造函数的子级构造函数
definition = this.options._base.extend(definition);
}
// 指令必须是对象的形式,如果在这里提供的是函数类型的话,则包装成对象的形式
if (type === 'directive' && typeof definition === 'function') {
definition = { bind: definition, update: definition };
}
this.options[type + 's'][id] = definition;
return definition
}
};
});
Vue.use
Vue.use的作用是注册插件。
/**
* 参数
* { Function | Object } plugin
*/
Vue.use = function (plugin) {// plugin 插件一般来说是一个实例对象
//插件只会被安装一次
if (plugin.installed) {
return
}
// arguments删除第一个参数后转换成真正的数组,并且添加this作为数组的第一个元素
var args = toArray(arguments, 1);
args.unshift(this);
// 判断use方法的第一个参数的install属性是否为函数,又或者第一个参数是否为函数,如果有一个满足则执行apply方法调用第一个参数函数,并且把use方法剩余的参数传入。
if (typeof plugin.install === 'function') {
plugin.install.apply(plugin, args);
} else if (typeof plugin === 'function') {
plugin.apply(null, args);
}
plugin.installed = true;
//返回this,能够实现链式调用
return this
};
Vue.mixin
Vue.mixin的作用是全局注入一个混入,影响注册之后创建的每个Vue.js实例。
其实现原理并不复杂,只是将用户传入的对象与vue.js自身的options属性合并在一起。
/**
* 参数
* { Object } minxin
*/
Vue.mixin = function (mixin) {
this.options = mergeOptions(this.options, mixin);
};
其中,mergeOptions方法会将用户传入的mixin与this.options合并成一个新对象,然后将这个生成的新对象覆盖this.options(即Vue.options)属性。
因为mixin方法修改了Vue.options属性,而之后创建的每个实例都会用到该属性,所以会影响创建的每个实例。
Vue.compile
Vue.compile用来编译模板字符串并返回包含渲染函数的对象。
并不是所有vue.js的构建版本都存在Vue.compile方法,Vue.compile只存在于完整版中。
Vue.compile方法只需要调用编译器就可以实现功能。其代码如下:
Vue.compile = compileToFunctions
其中compileToFunctions方法可以将模板编译成渲染函数。
Vue.version
Vue.version提供字符串形式的Vue.js安装版本号。可以根据不同版本号采取不同的策略。
Vue.version是一个属性。在构建文件的过程中,会读取package.json文件中的version,并将读取出的版本号设置到Vue.version上。
具体实现步骤是:Vue.js在构建文件的配置中定义了__VERSION__常量,使用rollup-plugin-replace插件在构建的过程中将代码中的常量__VERSION__替换成package.json文件中的版本号。
rollup-plugin-replace插件的作用是在构建过程中替换字符串。所以在代码中只需将__VERSION__赋值给Vue.version就可以在构建时将package.json文件中的版本号赋值给Vue.version。源码如下
Vue.version = '__VERSION__'
Vue.nextTick
在下次DOM更新循环结束之后执行延迟回调,修改数据之后立即使用这个方法获取更新后的DOM。
Vue.nextTick的实现原理与我们前面介绍的vm.$nextTick一样,代码如下:
/**
* 参数
* { Function } [callback]
* { Object } [context]
*/
import { nextTick } from '../util/index'
Vue.nextTick = nextTick
nextTick方法是封装的公共方法,供Vue.nextTick与vm.$nextTick共同调用。
Vue.set
Vue.set用于设置对象的属性。如果对象是响应性的,确保属性被创建后也是响应性的,同时触发视图更新。这个方法主要用于避开Vue不能检测属性被添加的限制。
Vue.set与vm.$set的实现原理相同,代码如下:
/**
* 参数
* { Object | Array } target
* { String | number } key
* { any } value
*/
import { set } from '../observer/index'
Vue.set = set
set 方法是封装的公共方法,供Vue.set与vm.$set共同调用。
Vue.delete
删除对象的属性。如果对象是响应性的,确保删除能触发视图更新。这个方法主要用于避开Vue不能检测属性被删除的限制。
Vue.delete与vm.$delete的实现原理相同,代码如下:
/**
* 参数
* { Object | Array } target
* { String | number } key/index
*/
import { del } from '../observer/index'
Vue.delete = del
del 方法是封装的公共方法,供Vue.delete与vm.$delete共同调用。
因nextTick、set、del方法已在Vue常用实例方法的实现原理一文中详细介绍了,故此处不在赘述。