Vue 初始化的过程-静态成员
在 src/core/index.js 这个文件中调用 initGlobalAPI 这个函数中初始化了 vue 的大多数静态成员。
initGlobalAPI
- 首先在方法中通过 defineProperty 给 Vue 挂载一个 config 对象属性
// config
const configDef = {}
configDef.get = () => config
if (process.env.NODE_ENV !== 'production') {
configDef.set = () => {
warn(
'Do not replace the Vue.config object, set individual fields instead.'
)
}
}
// 初始化 Vue.config 对象
Object.defineProperty(Vue, 'config', configDef)
- 然后是挂载静态方法 set/delete/nextTick
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
- 创建 options 对象,为了提高性能,将其原型设置为 null
// ASSET_TYPES = ['components', 'directives', 'filters']
// 初始化 Vue.options 对象,设置对象的原型为 null(提高性能) ,并给其扩展
// components/directives/filters/
// Vue 注册的全局的组件指令和过滤器都会分别保存在这三个对象下
Vue.options = Object.create(null)
ASSET_TYPES.forEach(type => {
Vue.options[type + 's'] = Object.create(null)
})
- 在 options 下面记录 Vue 构造函数
// this is used to identify the "base" constructor to extend all plain-object
// components with in Weex's multi-instance scenarios.
Vue.options._base = Vue
这个构造函数后面会用到,这里先不写,后面什么时候看到这个地方来再来填坑。
- 调用 extend 方法将 builtInComponents 的所有属性拷贝给 Vue.options.components 这个对象。
// 设置 keep-alive 组件
extend(Vue.options.components, builtInComponents)
这个地方就是注册了全局组件 keep-alive
- 注册 Vue.use() 静态方法
// 注册 Vue.use() 用来注册插件
initUse(Vue)
下面是 initUse 的实现源码
/* @flow */
import { toArray } from '../util/index'
export function initUse (Vue: GlobalAPI) {
Vue.use = function (plugin: Function | Object) {
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
// additional parameters
// 把数组中的第一个元素(plugin)删除
const args = toArray(arguments, 1)
// 把 this(Vue) 插到第一个元素的位置
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
}
}
- 在 initUse 中首先判断 plugins 的缓存数组是否存在,如果不存在,创建插件缓存数组,以存储已经注册过的插件防止重复注册。
- 判断插件是否已经注册过了,如果注册过了 return false
- 存储将要传给即将注册的插件的参数,并将 Vue 实例 unshift 到参数数组中。
- 判断 use 的插件是否有 install 方法,如果有使用 apply 调用这个 install 方法,并将前面存储好的参数数组作为参数传给 install 方法。
- 如果没有 install 方法,而是直接传入的一个函数,那就直接通过 apply 调用这个函数,并将前面存储好的参数数组作为参数传给这个函数。
- 最后将注册的插件 push 到插件缓存数组中,然后将 Vue 实例返回。
通过上述源码阅读,可以发现,Vue 插件可以是一个函数的形式,也可以是含有 install 方法的对象。
- 注册 Vue.mixin() 静态方法
// 注册 Vue.mixin() 实现混入
initMixin(Vue)
下面是 initMixin 的源码
/* @flow */
import { mergeOptions } from '../util/index'
export function initMixin (Vue: GlobalAPI) {
Vue.mixin = function (mixin: Object) {
this.options = mergeOptions(this.options, mixin)
return this
}
}
在这个方法中直接调用 mergeOptions 这个方法,将我们要混入的选项,合并到 Vue 的全局选项 options 中
- 注册 Vue.extend() 静态方法
// 注册 Vue.extend() 基于传入的 options 返回一个组件的构造函数
initExtend(Vue)
下面是 initExtend 的源码
/* @flow */
import { ASSET_TYPES } from 'shared/constants'
import { defineComputed, proxy } from '../instance/state'
import { extend, mergeOptions, validateComponentName } from '../util/index'
export function initExtend (Vue: GlobalAPI) {
/**
* Each instance constructor, including Vue, has a unique
* cid. This enables us to create wrapped "child
* constructors" for prototypal inheritance and cache them.
*/
Vue.cid = 0
let cid = 1
/**
* Class inheritance
*/
Vue.extend = function (extendOptions: Object): Function {
extendOptions = extendOptions || {}
// Vue 构造函数
const Super = this
const SuperId = Super.cid
// 从缓存中加载组件的构造函数
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
const name = extendOptions.name || Super.options.name
if (process.env.NODE_ENV !== 'production' && name) {
// 如果是开发环境验证组件的名称
validateComponentName(name)
}
const Sub = function VueComponent (options) {
// 调用 _init() 初始化
this._init(options)
}
// 原型继承自 Vue
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.cid = cid++
// 合并 options
Sub.options = mergeOptions(
Super.options,
extendOptions
)
Sub['super'] = Super
// For props and computed properties, we define the proxy getters on
// the Vue instances at extension time, on the extended prototype. This
// avoids Object.defineProperty calls for each instance created.
if (Sub.options.props) {
initProps(Sub)
}
if (Sub.options.computed) {
initComputed(Sub)
}
// allow further extension/mixin/plugin usage
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
// create asset registers, so extended classes
// can have their private assets too.
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
// enable recursive self-lookup
if (name) {
// 把组件的构造函数保存到 Ctor.options.components.comp = Ctor
Sub.options.components[name] = Sub
}
// keep a reference to the super options at extension time.
// later at instantiation we can check if Super's options have
// been updated.
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)
// cache constructor
// 把组件的构造函数缓存到 options._Ctor
cachedCtors[SuperId] = Sub
return Sub
}
}
function initProps (Comp) {
const props = Comp.options.props
for (const key in props) {
proxy(Comp.prototype, `_props`, key)
}
}
function initComputed (Comp) {
const computed = Comp.options.computed
for (const key in computed) {
defineComputed(Comp.prototype, key, computed[key])
}
}
这个文件的代码比较多,主要就是创建了一个构造函数 VueComponent 即组件的构造函数,并且这个构造函数原型继承自 Vue,所以所有的 Vue 组件都是继承自 Vue 。然后再构造函数中调用了继承自 Vue 的 _init 这个方法创建组件实例。最终将这个构造函数返回。
- 注册 Vue.directive()、Vue.component()、Vue.filter() 静态方法
// 注册 Vue.directive()、Vue.component()、Vue.filter()
initAssetRegisters(Vue)
initAssetRegisters 方法的源码如下
/* @flow */
import { ASSET_TYPES } from 'shared/constants'
import { isPlainObject, validateComponentName } from '../util/index'
export function initAssetRegisters (Vue: GlobalAPI) {
/**
* Create asset registration methods.
*/
// 遍历 ASSET_TYPES 数组,为 Vue 定义相应方法
// ASSET_TYPES 包括了 directive、component、filter
ASSET_TYPES.forEach(type => {
Vue[type] = function (
id: string,
definition: Function | Object
): Function | Object | void {
if (!definition) {
return this.options[type + 's'][id]
} else {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && type === 'component') {
validateComponentName(id)
}
// Vue.component('comp', { template: '' })
if (type === 'component' && isPlainObject(definition)) {
definition.name = definition.name || id
// 把组件的配置转换为组件的构造函数
definition = this.options._base.extend(definition)
}
if (type === 'directive' && typeof definition === 'function') {
definition = { bind: definition, update: definition }
}
// 全局注册,存储资源并赋值
// this.options['components']['comp'] = definition
this.options[type + 's'][id] = definition
return definition
}
}
})
}
通过遍历 ASSET_TYPES 这个常量数组,分别给 Vue 注册三个静态方法。