Mastering the Vue life cycle will make the difference in daily development.
Image from Vue official website
The principle of
The entrance
1. Call the _init method to start the life cycle as an entry. The file path is 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");
}
// Initialize the life cycle
this._init(options);
}
Copy the code
BeforeCreate, created
Find the _init method, file path: core/instance/init,
BeforeCreate: rarely used for instance initialization of life cycle properties, events, and component creation, but bidirectional binding systems are not available.
Created: Called after the instance has been created, the bidirectional binding system has been initialized, but **$el has not been mounted. This is where you can request that the data be prepared for subsequent data rendering. **
Delete part of source codeexport function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options? :Object) {
const vm: Component = this
vm._self = vm
initLifecycle(vm) // Initialize the lifecycle of the current VM
initEvents(vm) // Initialize the event
initRender(vm) // vm.$slots creates the element
callHook(vm, 'beforeCreate') // With the basic information for the component ready, call the beforeCreate hook function
initInjections(vm) // resolve injection before data/props injection before some values
initState(vm) // Initialize state
initProvide(vm) // resolve provide after data/props hang user-provided provide on it
callHook(vm, 'created') // Call the created hook function
// Start mounting elements if there are el elements, then find the following beforeMount and Mounted lifecycle hooks
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
export function initLifecycle (vm: Component) {
const options = vm.$options
// locate first non-abstract parent
let parent = options.parent // $parent
if(parent && ! options.abstract) {// The parent is not an abstract component, and the abstract component is not included in the parent-child keep-alive relationship
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent
}
parent.$children.push(vm) // Make the parent instance remember the current component instance
}
vm.$parent = parent // Add the $parent attribute to the parent instance
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
}
export function callHook (vm: Component, hook: string) {
// #7573 disable dep collection when invoking lifecycle hooks
pushTarget()
const handlers = vm.$options[hook]
const info = `${hook} hook`
if (handlers) {
for (let i = 0, j = handlers.length; i < j; i++) {
invokeWithErrorHandling(handlers[i], vm, null, vm, info)
}
}
if (vm._hasHookEvent) {
vm.$emit('hook:' + hook)
}
popTarget()
}
Copy the code
BeforeMount, mounted
Find the vm.$mount method and start mounting elements
BeforeMount: Called before the mount starts to check whether the current mounted parameters are valid.
BeforeUpdate: Here beforeUpdate is for listening, as described later
Mounted: This hook is invoked after the vm.$el is mounted to the instance. So you can do DOM manipulation
After this phase, Vue is fully functional, and when data changes, the subsequent update/uninstall life cycle is triggered
// mountComponent is called
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
) :Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
path:core/instance/lifecycle
export function mountComponent (vm: Component, el: ? Element, hydrating? : boolean) :Component {
vm.$el = el
if(! vm.$options.render) { vm.$options.render = createEmptyVNodeif(process.env.NODE_ENV ! = ='production') {
/* istanbul ignore if */
if ((vm.$options.template && vm.$options.template.charAt(0)! = =The '#') ||
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
)
}
}
}
// What is your environment before mounting it
callHook(vm, 'beforeMount')
let updateComponent
/* istanbul ignore if */
if(process.env.NODE_ENV ! = ='production' && config.performance && mark) {
// The component is mounted here
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) {// Trigger beforeUpdate before component updates,
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
// Calls the hook of the component that has been mounted
callHook(vm, 'mounted')}return vm
}
Copy the code
BeforeUpdate, updated
DOM updates are triggered when the data mode changes
BeforeUpdate: Called when data is updated and occurs before the virtual DOM is re-rendered and patched. You can further change the state in this hook without triggering additional rerendering.
Updated: This hook is called after the virtual DOM is re-rendered and patched due to data changes. You can perform DOM-dependent operations. In most cases, however, you should avoid changing the state during this period, as this can lead to an infinite update loop. This hook is not called during server-side rendering. Destroyed performs optimization operations, clears timers, and unbinds events.
// override Vue.prototype._update
function updateVirtualComponent (vnode? : VNode) {
const vm: Component = this
const componentId = vm.$options.componentId
if (vm._isMounted) {
callHook(vm, 'beforeUpdate')
}
vm._vnode = vnode
if (vm._isMounted && componentId) {
// TODO: data should be filtered and without bindings
const data = Object.assign({}, vm._data)
updateComponentData(componentId, data, () = > {
callHook(vm, 'updated')}}}Copy the code
BeforeDestroy, destroyed
When a component is about to unload, the file path is triggered: core/vdom/ create-Component
BeforeDestroy: Called before instance destruction. At this step, the instance is still fully available.
Destroyed: Called after the instance is destroyed. When called, everything indicated by the Vue instance is unbound, all event listeners are removed, and all subinstances are destroyed. This hook is not called during server-side rendering.
destroy (vnode: MountedComponentVNode) {
const { componentInstance } = vnode
if(! componentInstance._isDestroyed) {if(! vnode.data.keepAlive) { componentInstance.$destroy() }else {
deactivateChildComponent(componentInstance, true /* direct */)
}
}
}
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
// Remove the child from the parent component
if(parent && ! parent._isBeingDestroyed && ! vm.$options.abstract) { remove(parent.$children, vm) }// teardown watchers
// All watches will be destroyed if there are any
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.
// Listen to the number of data --
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}}Copy the code
Activated or deactivated
Two special life cycles
keep-alive
It abstracts the component without rendering anything to the DOM, just adding additional behavior.
Props: include-string or regular expression. Only the component name (the name attribute) that matches the name is cached. Exclude - a string or regular expression. Any component with a matching name (the name attribute) will not be cached. Max - Numbers. The maximum number of component instances can be cached, optimized using the LRU algorithm, and components that have not been used for a long time will be destroyed when exceeding the Max. <keep-alive> is a built-in component of Vue that keeps state in memory during component switching, preventing repeated DOM rendering. <keep-alive> when wrapping dynamic components, inactive component instances are cached rather than destroyed. There are two more lifecycle hooks activated that trigger deactivated on exit.Copy the code
When we use the abstract keep-alive component, the hook sequence is created-> Mounted -> activated when the page is first entered, and deactivated when the hook exits. When re-entering (forward or backward), only activated is triggered.
Activated: Triggers the hook when activated
Deactivated: Activated when switching to another dynamic component
export function activateChildComponent (vm: Component, direct? : boolean) {
if (direct) {
vm._directInactive = false
if (isInInactiveTree(vm)) {
return}}else if (vm._directInactive) {
return
}
if (vm._inactive || vm._inactive === null) {
vm._inactive = false
for (let i = 0; i < vm.$children.length; i++) {
activateChildComponent(vm.$children[i])
}
callHook(vm, 'activated')}}export function deactivateChildComponent (vm: Component, direct? : boolean) {
if (direct) {
vm._directInactive = true
if (isInInactiveTree(vm)) {
return}}if(! vm._inactive) { vm._inactive =true
for (let i = 0; i < vm.$children.length; i++) {
deactivateChildComponent(vm.$children[i])
}
callHook(vm, 'deactivated')}}Copy the code
If you don’t understand anything, or if there are any inadequacies or mistakes in my article, please point them out in the comments section. Thank you for reading.