大致可以分为四个阶段
- 初始化阶段:为Vue实例上初始化一些属性,事件以及响应式数据
- 模板编译阶段: 将模板编译成渲染函数
- 挂载阶段:将模板渲染到真实的DOM中,挂载指定的DOM上
- 销毁阶段:将实例自身从父组件中删除,并取消依赖追踪及事件监听器
mergeOptions 合并属性
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
定义在 src/core/util/options.js
中
/*
这个函数挺常用的
合并options
把parent和child这两个对象,根据合并策略,
合并成一个新的对象,并且返回
*/
export function mergeOptions (
parent: Object,
child: Object,
vm?: Component
): Object {
if (process.env.NODE_ENV !== 'production') {
checkComponents(child)
}
if (typeof child === 'function') {
child = child.options
}
/* 规范化 */
normalizeProps(child, vm)
normalizeInject(child, vm)
normalizeDirectives(child)
/* 这里是递归调用,有extends 或者 mixins */
if (!child._base) {
if (child.extends) {
parent = mergeOptions(parent, child.extends, vm)
}
if (child.mixins) {
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)
}
}
function mergeField (key) {
/*
strat 实际上是一个函数,通过不同的key,去用不同的函数
根据不同的key 是有不同的合并策略的
设计模式中的【策略】模式
*/
const strat = strats[key] || defaultStrat
/* 这里进行合并 */
options[key] = strat(parent[key], child[key], vm, key)
}
return options
}
callHook函数如何触发钩子函数
/*
callhook 函数,
触发生命周期钩子函数
比较简单,就是遍历数组,执行钩子函数
*/
export function callHook (vm: Component, hook: string) {
// #7573 disable dep collection when invoking lifecycle hooks
pushTarget()
/*
handlers 这里其实是个数组
*/
const handlers = vm.$options[hook]
const info = `${hook} hook`
if (handlers) {
for (let i = 0, j = handlers.length; i < j; i++) {
// 在这个函数里面执行call, 钩子函数
/* 遍历数组,把每一个钩子函数都执行一遍 */
invokeWithErrorHandling(handlers[i], vm, null, vm, info)
}
}
if (vm._hasHookEvent) {
/* 这里触发hook, 是不是在这里挂载的 */
vm.$emit('hook:' + hook)
}
popTarget()
}
initLifecycle
就是给实例初始化了一些属性,包括以$
开头的供用户使用的外部属性,也包括以_
开头的供内部使用的内部属性
/*
代码不多
主要是挂载了一些默认属性
$parent
$root
*/
export function initLifecycle (vm: Component) {
const options = vm.$options
// locate first non-abstract parent
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 = {}
vm._watcher = null
vm._inactive = null
vm._directInactive = false
vm._isMounted = false
vm._isDestroyed = false
vm._isBeingDestroyed = false
}
initEvents
<child @select="selectHandler" @click.native="clickHandler"></child>
本篇文章介绍了生命周期初始化阶段所调用的第二个初始化函数——initEvents
。该函数是用来初始化实例的事件系统的。
我们先从模板编译时对组件标签上的事件解析入手分析,我们知道了,父组件既可以给子组件上绑定自定义事件,也可以绑定浏览器原生事件。这两种事件有着不同的处理时机,浏览器原生事件是由父组件处理,而自定义事件是在子组件初始化的时候由父组件传给子组件,再由子组件注册到实例的事件系统中。
也就是说:初始化事件函数initEvents实际上初始化的是父组件在模板中使用v-on或@注册的监听子组件内触发的事件。
最后分析了initEvents
函数的具体实现过程,该函数内部首先在实例上新增了_events
属性并将其赋值为空对象,用来存储事件。接着通过调用updateComponentListeners
函数,将父组件向子组件注册的事件注册到子组件实例中的_events
对象里