# 生命周期
Vue对象的生命周期流程如下图所示。

# create阶段
典型的是通过new Vue(options)创建Vue实例的过程
import("../vue.study.esm.js").then((module) => {
const { Vue } = module;
const app = new Vue({});
});
通过单步调试可以看到最终会执行src/core/instance/init.ts中_init方法,观察vm变化,可以看到vm被添加_uid、$options等属性和方法。注意beforeCreate和created钩子调用时机。
export function initMixin(Vue: Component) {
Vue.prototype._init = function (options?: Record<string, any>) {
const vm: Component = this
// a uid
vm._uid = uid++
let startTag, endTag
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
mark(startTag)
}
// a flag to avoid this being observed
vm._isVue = true
// 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 as any)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor as any),
options || {},
vm
)
}
/* istanbul ignore else */
if (process.env.NODE_ENV !== 'production') {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose real self
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
vm._name = formatComponentName(vm, false)
mark(endTag)
measure(`vue ${vm._name} init`, startTag, endTag)
}
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}

# mount阶段
通过vm.$mount调用来实现mount。
import("../vue.study.esm.js").then((module) => {
const { Vue } = module;
const app = new Vue({
render(h) {
return h("span", null, "Test");
},
});
app.$mount("#app");
});
通过单步调试可以看到最终会执行src/core/instance/lifecycle.ts中mountComponent方法,通过一个绑定_update和_render方法Watcher来实现首次渲染和动态渲染。注意beforeMount、beforeUpdate和mounted
export function mountComponent(
vm: Component,
el: Element | null | undefined,
hydrating?: boolean
): Component {
vm.$el = el;
if (!vm.$options.render) {
// @ts-expect-error invalid type
vm.$options.render = createEmptyVNode;
if (process.env.NODE_ENV !== "production") {
/* istanbul ignore if */
if (
(vm.$options.template && vm.$options.template.charAt(0) !== "#") ||
vm.$options.el ||
el
) {
warn(
"You are using the runtime-only build of Vue where the template " +
"compiler is not available. Either pre-compile the templates into " +
"render functions, or use the compiler-included build.",
vm
);
} else {
warn(
"Failed to mount component: template or render function not defined.",
vm
);
}
}
}
callHook(vm, "beforeMount");
let updateComponent;
/* istanbul ignore if */
if (process.env.NODE_ENV !== "production" && config.performance && mark) {
updateComponent = () => {
const name = vm._name;
const id = vm._uid;
const startTag = `vue-perf-start:${id}`;
const endTag = `vue-perf-end:${id}`;
mark(startTag);
const vnode = vm._render();
mark(endTag);
measure(`vue ${name} render`, startTag, endTag);
mark(startTag);
vm._update(vnode, hydrating);
mark(endTag);
measure(`vue ${name} patch`, startTag, endTag);
};
} else {
updateComponent = () => {
vm._update(vm._render(), hydrating);
};
}
// we set this to vm._watcher inside the watcher's constructor
// since the watcher's initial patch may call $forceUpdate (e.g. inside child
// component's mounted hook), which relies on vm._watcher being already defined
new Watcher(
vm,
updateComponent,
noop,
{
before() {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, "beforeUpdate");
}
},
},
true /* isRenderWatcher */
);
hydrating = false;
// manually mounted instance, call mounted on self
// mounted is called for render-created child components in its inserted hook
if (vm.$vnode == null) {
vm._isMounted = true;
callHook(vm, "mounted");
}
return vm;
}
# update阶段
在渲染Watcher上实现update。参考上面可知beforeUpdate绑定的before上,而updated在src/core/observer/scheduler.ts中callUpdatedHooks调用。
import("../vue.study.esm.js").then((module) => {
const { Vue } = module;
const app = new Vue({
data() {
return {
value: 1,
};
},
render(h) {
const that = this
return h(
"span",
{
on: {
click() {
that.value++;
},
},
},
`Count ${this.value}`
);
},
});
app.$mount("#app");
});
执行Watcher.run队列后会根据是否为渲染Watcher判处执行update钩子。
function callUpdatedHooks(queue) {
let i = queue.length
while (i--) {
const watcher = queue[i]
const vm = watcher.vm
if (vm._watcher === watcher && vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'updated')
}
}
}
# destroy阶段
通过vm.$destroy调用实现,实际情况中在组件_update时会被调用,这个将在后续内容讨论。
let app;
import("../vue.study.esm.js").then((module) => {
const { Vue } = module;
app = new Vue({
data() {
return {
value: 1,
};
},
render(h) {
const that = this
return h(
"span",
{
on: {
click() {
// 需要注意的是Vue对象销毁后并不会直接影响其在页面上的显示
// (显示内容不会直接消失,但相关响应式功能已停止)
that.$destroy();
},
},
},
`Count ${this.value}`
);
},
});
app.$mount("#app");
});注意观察销毁之后的情况,在$destroy中销毁了DOM上的事件监听。
Vue.prototype.$destroy = function () {
const vm: Component = this;
if (vm._isBeingDestroyed) {
return;
}
callHook(vm, "beforeDestroy");
vm._isBeingDestroyed = true;
// remove self from parent
const parent = vm.$parent;
if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
remove(parent.$children, vm);
}
// teardown watchers
if (vm._watcher) {
vm._watcher.teardown();
}
let i = vm._watchers.length;
while (i--) {
vm._watchers[i].teardown();
}
// remove reference from data ob
// frozen object may not have observer.
if (vm._data.__ob__) {
vm._data.__ob__.vmCount--;
}
// call the last hook...
vm._isDestroyed = true;
// invoke destroy hooks on current rendered tree
vm.__patch__(vm._vnode, null);
// fire destroyed hook
callHook(vm, "destroyed");
// turn off all instance listeners.
vm.$off();
// remove __vue__ reference
if (vm.$el) {
vm.$el.__vue__ = null;
}
// release circular reference (#6759)
if (vm.$vnode) {
vm.$vnode.parent = null;
}
};