本文分析的 Vue 源码版本是 2.6.10
Vue 官网第一个🌰就是:
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
通过new的方式创建了一个Vue的实例 app,接下来我们看一下这个Vue到底做了什么?
new Vue
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)
}
我们可以看到 Vue 通过 new 关键字初始化,然后调用 this._init 方法
this._init()
src/core/instance/init.js中:
Vue.prototype._init = function (options?: Object) {
// merge options
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
}
...
// 初始化生命周期
initLifecycle(vm)
// 初始化事件中心
initEvents(vm)
// 初始化渲染
initRender(vm)
// 调用beforeCreate钩子函数
callHook(vm, 'beforeCreate')
// 初始化inject
initInjections(vm) // resolve injections before data/props
// 初始化状态
initState(vm)
// 初始化provide
initProvide(vm) // resolve provide after data/props
// 调用ceated钩子函数
callHook(vm, 'created')
...
// 检测到如果有 el 属性,则调用 vm.$mount 方法挂载 vm,挂载的目标就是把模板渲染成最终的 DOM
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
此段代码对应的是生命周期图示
接下来我们详细分析一下 Vue 的初始化过程
初始化过程
-
初始化生命周期(挂载私有属性)
src/core/instance/lifecycle.js中:export function initLifecycle (vm: Component) { const options = vm.$options // locate first non-abstract parent // 在keep-alive中,会设置 abstract: true 属性,Vue 会跳过该组件实例 let parent = options.parent if (parent && !options.abstract) { while (parent.$options.abstract && parent.$parent) { parent = parent.$parent } parent.$children.push(vm) } // 为组件实例挂载相应属性,并初始化 vm.$parent = parent vm.$root = parent ? parent.$root : vm vm.$children = [] vm.$refs = {} // watcher 实例对象 vm._watcher = null // keep-alive 组件激活状态 vm._inactive = null // keep-alive 组件状态属性 vm._directInactive = false // 实例是否完成挂载(对应生命周期图示中的mounted) vm._isMounted = false // 实例是否完成销毁(对应生命周期图示中的destroyed) vm._isDestroyed = false // 实例是否正在销毁(介于生命周期图示中deforeDestroy和destroyed之间) vm._isBeingDestroyed = false } -
初始化事件中心
src/core/instance/events.jsexport function initEvents (vm: Component) { // 创建事件对象,存储事件 vm._events = Object.create(null) // 系统事件标识位 vm._hasHookEvent = false // init parent attached events const listeners = vm.$options._parentListeners if (listeners) { updateComponentListeners(vm, listeners) } } -
初始化渲染(初始化
$slot、$createElement、$attrs、$listeners等渲染属性)src/core/instance/render.js中:export function initRender (vm: Component) { ... // 处理组件slot,返回slot插槽对象 vm.$slots = resolveSlots(options._renderChildren, renderContext) vm.$scopedSlots = emptyObject // bind the createElement fn to this instance // so that we get proper render context inside it. // args order: tag, data, children, normalizationType, alwaysNormalize // internal version is used by render functions compiled from templates vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false) // normalization is always applied for the public version, used in // user-written render functions. vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true) // $attrs & $listeners are exposed for easier HOC creation. // they need to be reactive so that HOCs using them are always updated const parentData = parentVnode && parentVnode.data /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { ... } else { defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true) defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true) } } -
调用了
beforeCreate钩子函数 -
初始化 inject (状态初始化之前)
src/core/instance/inject.js中:export function initInjections (vm: Component) { const result = resolveInject(vm.$options.inject, vm) if (result) { toggleObserving(false) Object.keys(result).forEach(key => { /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { defineReactive(vm, key, result[key], () => { warn( `Avoid mutating an injected value directly since the changes will be ` + `overwritten whenever the provided component re-renders. ` + `injection being mutated: "${key}"`, vm ) }) } else { defineReactive(vm, key, result[key]) } }) toggleObserving(true) } } -
初始化状态
在这里我们不详细分析
inState方法,详细分析请看《Vue.js 源码分析二:initState 原理》。initState方法主要是对props、methods、data、computed和wathcer等属性做了初始化操作。 -
初始化 provide(状态初始化之后)
src/core/instance/inject.js中:export function initProvide (vm: Component) { const provide = vm.$options.provide if (provide) { vm._provided = typeof provide === 'function' ? provide.call(vm) : provide } } -
调用了
created钩子函数
总结
Vue 初始化主要就干了几件事情,合并配置,初始化生命周期,初始化事件中心,初始化渲染,初始化状态。