The most difficult thing is to know yourself
preface
In Vue, each Vue instance goes through a process from creation to destruction, just like a human, from birth to death. A lot of things happen during this process, such as setting up data listeners, compiling templates, mounting components, etc. In Vue, the process of creating a Vue instance from its creation to its destruction is called the Vue instance life cycle, and Vue also provides different hook functions at different stages of the Vue instance life cycle, so that users can do extra things at different stages of the Vue instance life cycle.
The body of the
Let’s take a look at the official life cycle diagram:
beforeCreate
When creating a Vue instance, this._init(options) is executed internally:
vm.$options = mergeOptions( // Merge the configuration
resolveConstructorOptions(vm.constructor),
options || {},
vm
)
initLifecycle(vm) // Initialize the life cycle
initEvents(vm) // Initialize the event center
initRender(vm) // Initialize render
callHook(vm, 'beforeCreate')
Copy the code
mergeOptions | Merge configuration | Combine Vue, user-defined, and instance properties, and the hook function merges into an array |
initLifecycle(vm) | Initialize the life cycle | Mount some properties to the Vue instance and set default values,$parent ,$root The more important |
initEvents(vm) | Initialize the event center | In the registration event from the parent component to the child component, the custom event is passed to the child component, which is initialized when the child component is instantiated. Browser native events are handled in the parent component. |
initRender(vm) | Initialize the render | Mount the Vue instance$slots, $scopedSlots, _c, $createElement And other attributes, will$attrs, $listeners Turn into responsive |
Loading effects can be added at this stage and removed when created
created
initInjections(vm) // Initialize inject
initState(vm) // Initialize state
initProvide(vm) // initialize provide
callHook(vm, 'created')
Copy the code
InitProvide (VM) injects attributes into the current component, and initInjections(VM) is the initinjection remedy: use the bottom-up method to find the corresponding attributes injected by the upstream parent component. InitState (VM) initializes five options in order: props, Methods, data, computed, and watch.
initProps(vm, opts.props) | Initialize the props | Make the props responsive and proxy to the Vue instance |
initMethods(vm, opts.methods) | Initialize the methods | Proxy methods to Vue instances |
initData(vm) | Initialize the data | Assign responsivity to data and proxy to Vue instances |
initComputed(vm, opts.computed) | Initialize the computed | Cycle to create a computed Watcher and collect it on the responsive properties that the handler depends on, and finally to put the computed proxy on the Vue instance |
initWatch(vm, opts.watch) | Initialize the watch | The loop creates the user Watcher and collects it on the responsive properties it listens to |
This phase can perform asynchronous requests for data methods, complete the initialization of the data
beforeMount
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
// src\platforms\web\entry-runtime-with-compiler.js
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (el, hydrating) {
el = el && query(el)
if (el === document.body || el === document.documentElement) { process.env.NODE_ENV ! = ='production' && warn(
`Do not mount Vue to <html> or <body> - mount to normal elements instead.`
)
return this
}
if(! options.render) {if (template) {
// Handle the selectors at the beginning of #, dom objects
} else if (el) { // Get el's outerHTML as the template
template = getOuterHTML(el)
}
if (template) { // Compile to generate the render functioncompileToFunctions(template, {... }}}return mount.call(this, el, hydrating)
}
// src\core\instance\lifecycle.js
export function mountComponent (vm, el, hydrating) {
vm.$el = el
if(! vm.$options.render) { vm.$options.render = createEmptyVNode } callHook(vm,'beforeMount')
/ /...
}
Copy the code
In this stage, we mainly did the following things:
- judge
el
If it exists, it will be called if it exists$mount
To perform subsequent mounting operations query(el)
getel
thedom
Element reference- right
el
Make a judgment, cannot beBody, HTML
The element - judge
render
Whether or not the function is definedtemplate
Processing and template compilation generationrender
function - in
Vue
Instance mount$el
attribute - judge
render
Whether the function is defined or null if notVNode
(The judgment here refers to the situation when the child component is mounted)
mounted
updateComponent = () = > {
vm._update(vm._render(), hydrating)
}
new Watcher(vm, updateComponent, noop, {
before () {
if(vm._isMounted && ! vm._isDestroyed) { callHook(vm,'beforeUpdate')}}},true /* isRenderWatcher */)
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')}return vm
Copy the code
In this stage, render Watcher is created, vm._render() generates VNode, and vm._update() performs patch rendering view.
In this stage, DOM is loaded and mounted. DOM operations can be performed.
beforeUpdate
// src\core\observer\scheduler.js
function flushSchedulerQueue () {
for (index = 0; index < queue.length; index++) {
watcher = queue[index]
if (watcher.before) {
watcher.before()
}
id = watcher.id
has[id] = null
watcher.run()
}
/ /...
callUpdatedHooks(updatedQueue)
}
Copy the code
Mounted () {beforeUpdate () {before ();}} before () {before ();}} before () {before ()}}}
updated
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')}}}Copy the code
In the beforeUpdate phase above, all Watcher calls callUpdatedHooks after executing the run method to loop through all Vue instances’ updated hook functions.
At this stage, data is updated and DOM is re-rendered. Unified processing of business can be carried out here.
beforeDestroy
// src\core\instance\lifecycle.js
Vue.prototype.$destroy = function () {
const vm: Component = this
if (vm._isBeingDestroyed) {
return
}
callHook(vm, 'beforeDestroy')
vm._isBeingDestroyed = true
const parent = vm.$parent
if(parent && ! parent._isBeingDestroyed && ! vm.$options.abstract) { remove(parent.$children, vm) }if (vm._watcher) {
vm._watcher.teardown()
}
let i = vm._watchers.length
while (i--) {
vm._watchers[i].teardown()
}
if (vm._data.__ob__) {
vm._data.__ob__.vmCount--
}
vm._isDestroyed = true
vm.__patch__(vm._vnode, null)
callHook(vm, 'destroyed')
vm.$off()
if (vm.$el) {
vm.$el.__vue__ = null
}
if (vm.$vnode) {
vm.$vnode.parent = null}}}Copy the code
We can see that the beforeDestroy hook function is called at the beginning of execution of this.$destroy(). When is this.$destroy() executed except when we call it manually?
🙋♂️ : Execute at the time of component destruction
🙎♀️ : When will the component be destroyed?
🙋♂️ : During patch phase, the component is destroyed when oldVNode needs to be removed during VNode rendering update
// src\core\vdom\create-component.js
var componentVNodeHooks = {
destroy: function destroy (vnode) {
var componentInstance = vnode.componentInstance;
if(! componentInstance._isDestroyed) {if(! vnode.data.keepAlive) { componentInstance.$destroy(); }else {
deactivateChildComponent(componentInstance, true /* direct */); }}}}Copy the code
When Vnode renders an update, it also fires the corresponding hook function at each stage while performing the related operation. When a component is destroyed, the destroy hook function is triggered to execute this.$destroy().
destroyed
The Destroyed phase does several things:
- Will the current
Vue
Instance removed from its parent instance - Unload from the current instance
watcher
vm.__patch__(vm._vnode, null)
againpatch
For manual callsthis.$destroy()
In the case
Note: The removal of all custom event listeners, vm.$off(), is done after the Destroyed hook function is executed.
Finally, references to some related properties are removed, and the component is destroyed.
At the end
Nothing to say, Aoli!!