-
Notifications
You must be signed in to change notification settings - Fork 0
Description
通过上文 Vue 源码学习(一) - 目录结构与构建,我们了解了整个项目的目录结构,这次我们就从构造函数开始,看 vue 到 export default Vue 都经历了什么。
声明:为了更好的理解 vue 源码,我们将以完整版 umd 模块的 vue 为入口
构造函数
根据 entry 配置,找到入口文件,
// src/platforms/web/entry-runtime-with-compiler.js
import config from 'core/config'
import { warn, cached } from 'core/util/index'
import { mark, measure } from 'core/util/pref'
import Vue from './runtime/index'
...根据文件的引用关系找到 Vue 的构造函数所在
// src/platforms/web/runtime/index.js
import Vue from 'core/index'
...// src/core/index.js
import Vue from './instance/index'
...// src/core/instance/index.js
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue最终我们在 src/core/instance/index.js 中找到了 Vue 的构造函数,内容部分也很简单,传入一个 options 参数,如果是非生产环境且没用使用 new Vue 的方式调用函数,给出警告,然后执行 _init 方法。
接着经过如下
- initMixin
- stateMixin
- eventsMixin
- lifecycleMixin
- renderMixin
5个方法的包装处理,最终对外抛出 Vue。
我们后面的分析,也是根据这几个方法的顺序来分析,他们内部到底做了哪些处理。
全局API
在此之前,我们再来看下
// src/core/index.js
import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
initGlobalAPI(Vue)
...通过上一篇文章的目录可知,global-api 是负责给 Vue 构造函数挂载全局的方法(静态方法)或属性的代码。它对我们后续的代码分析很重要,所以在分析 _init 前,我们不妨先看看 global-api 具体挂载了哪些方法和属性呢?
// src/core/global-api/index.js
import config from '../config'
import { initUse } from './use'
import { initMixin } from './mixin'
import { initExtend } from './extend'
import { initAssetRegisters } from './assets'
import { set, del } from '../observer/index'
import { ASSET_TYPES } from 'shared/constants'
import builtInComponents from '../components/index'
import { observe } from 'core/observer/index'
export function initGlobalAPI (Vue: GlobalAPI) {
// 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.'
)
}
}
Object.defineProperty(Vue, 'config', configDef)
// exposed util methods.
// NOTE: these are not considered part of the public API - avoid relying on
// them unless you are aware of the risk.
Vue.util = {
warn,
extend,
mergeOptions,
defineReactive
}
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
// 2.6 explicit observable API
Vue.observable = <T>(obj: T): T => {
observe(obj)
return obj
}
Vue.options = Object.create(null)
ASSET_TYPES.forEach(type => {
Vue.options[type + 's'] = Object.create(null)
})
// 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(Vue.options.components, builtInComponents)
initUse(Vue)
initMixin(Vue)
initExtend(Vue)
initAssetRegisters(Vue)
}我们来逐行看下代码,首先是在构造函数 Vue 上增加了一个 config 的静态只读属性,读取 config 返回的是 core/config.js 中导出的对象,如果试图修改该值时,在非生产环境下会给一个警告。
接着是扩展了 util 属性,增加了 warn,extend,mergeOptions 和 defineReactive 四个方法。
Vue.util = {
warn,
extend,
mergeOptions,
defineReactive
}然后是在 Vue 上增加了 set,delete 以及 nextTick 三个方法,同时在 2.6 以上版本,新增了 observable 方法,原本必须在一个 Vue 实例中配置的响应对象,现在可以在 Vue 实例外部通过 Vue.observable(data) 来创建了。
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
// 2.6 explicit observable API
Vue.observable = <T>(obj: T): T => {
observe(obj)
return obj
}下面是对 options 属性的创建,后面的 options 合并章节中起着关键的作用。
Vue.options = Object.create(null)
ASSET_TYPES.forEach(type => {
Vue.options[type + 's'] = Object.create(null)
})
// 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首先是创建一个空对象,然后遍历 src/shared/constants.js 中 ASSET_TYPES 数组,分创建一个空对象,
// src/shared/constants.js
export const ASSET_TYPES = [
'component',
'directive',
'filter'
]经过处理后的 options 最终变成:
Vue.options = {
components: Object.create(null),
directives: Object.create(null),
filters: Object.create(null),
_base: Vue
}紧接着的这句代码:
extend(Vue.options.components, builtInComponents)把 builtInComponents 返回的值与 Vue.options.components 进行合并操作,
// src/core/components/index.js
import KeepAlive from './keep-alive'
export default {
KeepAlive
}最终 Vue.options 变为:
Vue.options = {
components: {
KeepAlive
},
directives: Object.create(null),
filters: Object.create(null),
_base: Vue
}在文件最后,还有4个 init* 方法,我们再来逐个看下每个方法都是干嘛的。
// src/core/global-api/use.js
export function initUse(Vue: GlobalAPI) {
Vue.use = function(plugin: Function | Object) {
...
}
}该方法的作用是在 Vue 构造函数上添加 use 方法,该方法是用来安装 Vue 插件的。
// src/core/global-api/mixin.js
export function initMixin (Vue: GlobalAPI) {
Vue.mixin = function (mixin: Object) {
this.options = mergeOptions(this.options, mixin)
return this
}
}该方法是在 Vue 上添加 mixin 方法,该方法是全局注册一个混入,并且会影响注册之后所有创建的每个 Vue 实例。
// src/core/global-api/extend.js
export function initExtend(Vue: GlobalAPI) {
Vue.cid = 0;
let cid = 1;
...
Vue.extend = function(extendOptions: Object): Function {
}
}该方法是在 Vue 上添加了 Vue.cid 静态属性和 extend 静态方法。该方法继承自 Vue 构造器,创建一个子类。
// src/core/global-api/assets.js
export function initAssetRegisters(Vue: GlobalAPI) {
ASSET_TYPES.forEach(type => {
Vue[type] = function() {
// ...
}
})
}ASSET_TYPES 我们已不陌生,就是上面提到的 component,directive 和 filter。该方法在 Vue 上增加了三个静态方法,他们分别用来全局注册组件,指令和过滤器。
我们再回溯到前面的文件,分别扩展了 directives 和 components:
// src/platforms/web/runtime/index.js
import platformDirectives from './directives/index'
import platformComponents from './components/index'
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)// src/platforms/web/runtime/directives/index.js
export default {
model,
show
}// src/platforms/web/runtime/components/index.js
export default {
Transition,
TransitionGroup
}小结
至此整个构造函数都讲解完了,主要是通过 global-api/index.js 这个文件,对 Vue 的构造函数增加了许多静态属性和方法,方便后续的使用。此时 Vue 上挂载的属性和方法如下:
Vue.config
Vue.util = {
warn,
extend,
mergeOptions,
defineReactive
}
Vue.set
Vue.delete
Vue.nextTick
Vue.observable
Vue.options = {
components: {
KeepAlive,
Transition,
TransitionGroup
},
directives: {
model,
show
},
filters: Object.create(null),
_base: Vue
}
Vue.use
Vue.mixin
Vue.extend
Vue.cid
Vue.components
Vue.directives
Vue.filters