Vue life cycle

1.The life cycle

The process of Vue instance from creation to destruction can be roughly divided into four stages according to the flowchart:

Initialization phase: initializes some properties, events, and response data for the VUE instance;

Template compilation stage: compile the template into a rendering function;

Mount stage: mount the instance to the specified DOM, that is, render the template to the real DOM;

Destruction phase: Remove the instance itself from the parent component and cancel dependency tracing and event listening;

2,Lifecycle functions (hooks)

A function that is automatically executed at a particular point in its life cycle.

3,Initialization phase

Create vue instance :new vue() == > Core code :this._init(options), _init(options)

Method of execution Introduction to the
initLifecycle(vm) Initialize the life cycle, which defines some of the variables and attributes. The main Settings
p a r e n t . The parent,
Children, $refs, _watcher, isMounted, isDestroyed, etc
initEvents(vm) Initializes the events that the parent component binds to the instance and the binding of the events (not DOM events). Basically defines
o n c e , Once,
Off,
e m i t , Emit,
on
initRender(vm) Initialize render related properties and methods, primarily defining the createElement function (to generate a virtual VNode)
callHook(vm, ‘beforeCreate’) Run the beforeCreate life cycle command
initInjections(vm) Initializes the Inject option in the instance
initState(vm) Perform data initialization, in which five options are initialized in order: props, Methods, data, computed, and Watch. In addition, complete data hijacking observe and configure watcher observer instances for computed and watch, so that when the data changes later, the data changes can be sensed and the page rendering can be completed
initProvide(vm) Bind the provide property to the provided. It comes in pairs with Inject, and runs after initState because provide may use data, props, methods, etc.
callHook(vm, ‘created’) Implementing Creadted lifecycle functions, manipulating data, methods, etc., can only be done in the earliest created lifecycle functions

4,Template compilation stage

At the end of the initialization phase, we determine if there is currently an el argument, and if not, we wait for the $mount(el) method to be called. Render (el) {render (el) {render (el) {render (el) {render (el) {render (el) {render (el) {render (el) {render (el) {render (el) {render (el) {render (el) {render (el) {render (el) {render (el) {render (el) {render (el) {render (el) {render (el)}}} If we don’t have a template, we compile the el (#app, which might have other tags inside it) directly into a templae, and then convert the template into a render function.

<div id="app">{{el}}</div>

<script>
	var app = new Vue({
		el: '#app'.data: {
			el: 'Render by EL'.template: 'Render by Template'.render: 'Render by render'
		},
		template: '<div>{{ template }}</div>'.render(h) {
			return h('div'.this.render); }});// The page is rendered by render
	// render -> template -> el
</script>
Copy the code

Whether you use el or template or the usual.vue file, you end up with the render function.

Vue is built on source code in two versions: the full version and the runtime version only

The only difference between the two versions is that the latter includes a compiler that can create vue instances, render and process virtual DOM, etc. With Vue-Loader or Vueift, templates are precompiled into rendering functions at build time, and the initialization phase goes directly to the mount phase. The template compilation phase only exists in the full version.

5,Mount the stage

With the Render function, trigger the beforeMount lifecycle hook function, enter the mount phase, and execute the updateComponent function. Partial source code:

callHook(vm, 'beforeMount');

let updateComponent;

updateComponent = function() {
	vm._update(vm._render(), hydrating);
};
Copy the code

Inside the function, vm._render() calls the render function to generate a new virtual DOM. The vue.prototype. _update method is passed to the component to perform rendering to the page.

_update Internal method patch determines whether it is the first rendering or update based on whether there is an old virtual DOM. If there is an old virtual DOM that was last rendered, the latest virtual DOM is compared with the old virtual DOM and the DOM node is updated. Then start rendering the render into the actual DOM. After rendering to the real DOM, the rendered real DOM is replaced with the original Vm.el. And then I’m going to replace el. And then I’m going to replace el. The replaced EL is then rendered into the view page.

If it is the first rendering, patch the vNode to help us create a new vNode using createElm and render it into the DOM node.

Then execute the monut lifecycle function and set _isMounted, an attribute that identifies the lifecycle, to true. In Mounted, we can manipulate the DOM because the DOM has already been rendered.

At this point, the mount operation is half complete, and the mount not only renders the template to the view, but also enables the monitoring of the data (state) in the template, notifying its dependencies to update the view when the data (state) changes.

export function mountComponent(vm, el, hydrating) {
	vm.$el = el;
	if(! vm.$options.render) { vm.$options.render = createEmptyVNode; } callHook(vm,'beforeMount');

	let updateComponent;

	updateComponent = () = > {
		vm._update(vm._render(), hydrating);
	};
	new Watcher(
		vm,
		updateComponent,
		noop,
		{
			before() {
				if (vm._isMounted) {
					callHook(vm, 'beforeUpdate'); }}},true /* isRenderWatcher */
	);
	hydrating = false;

	if (vm.$vnode == null) {
		vm._isMounted = true;
		callHook(vm, 'mounted');
	}
	return vm;
}
Copy the code

As you can see from the mounted source code, you create an instance of Watcher and pass in the defined updateComponent function. To enable monitoring of data (state) in templates.

BeforeUpdate life cycle function is triggered when our state data changes to start rendering our changed data to the page (check _isMounted and _isDestroyed is false) Ensure that the DOM has been mounted and the current component has not been destroyed.

BeforeUpdate after the call, we create a new virtual DOM (Vnode) and perform a diff calculation with the latest Vnode and the original Vnode. This involves a series of calculations to figure out the minimum update range. To update the latest data in the render function, and render the updated render function into the real DOM. That completes our data update

Then execute updated, so the updated can also operate dom and get the latest updated DOM.

Mouted and updated do not wait for all child components to be mounted. Vue performs DOM updates asynchronously and opens a Queue as soon as data changes are observed. It then pushes the watcher that observed the change in the same event loop to the Queue. If the watcher is triggered multiple times, it will only be pushed to the queue once. This buffering behavior effectively eliminates unnecessary computation and DOm manipulation caused by duplicate data. During the next event loop, Vue empties the queue and performs the necessary DOM updates.

Such as:

<template> <div> <ul ref="list"> <li v-for="(item, Index) in list" :key="index">{{item}}</li> </ul> <button @click=" addItem "> add </button> </div> </template> <script> Export default {data() {return {list: [' first ', 'second ',' third ']}; }, methods: {additem() {this.list.push(' add one '); This.list.push (' add one '); This.list.push (' add one '); let child = this.$refs.list.childNodes.length; Console. log('list length :', child); 3 this. / / the result is $nextTick (() = > {child = this. Let $refs. List..childnodes. Length; Console. log('list length :', child); // Result is 6}); }}}; </script>Copy the code

When the button is clicked, the list loops out to six, but only three are retrieved from the DOM, and the DOM hasn’t been updated yet. If you want to get the updated status of the DOM after modifying the data, you can use the this.$nextTick method.

This.$nextTick(): Executes a deferred callback after the next DOM update loop ends. Use this method immediately after modifying the data to get the updated DOMCopy the code

6,Destruction of phase

When the vM. $destroy method is called, the Vue instance enters the destruction phase, which removes the current Vue instance from its parent, undoes all dependency tracing on the current instance, and removes all event listeners on the instance.

BeforeDestroy the life cycle is a function in which the instance can still be manipulated before it is destroyed.

It then does a series of destruction actions, removing various data references, removing event listeners, deleting component _watcher, deleting subinstances, deleting self, and so on. Also set the instance attribute _isDestroyed to true

After the destruction is complete, you will no longer be able to operate the instance. Life cycle the whole process ends.

Conclusion:

The life cycle Introduction to the
beforeCreated generate
o p t i o n s Option and add lifecycle related properties to the instance. After instance initialization, the data is observed ( d a t a o b s e r v e r ) and e v e n t / w a t c h e r Before the event configuration is called, that is, d a t a . w a t c h e r . m e t h o d s It doesn’t exist. But there is an object, and that is Options and add lifecycle related properties to the instance. Called after instance initialization and before data Observer and Event/Watcher event configuration, that is, data, Watcher, and Methods do not exist at this stage. But there is an object, and that is
Route, so that operations such as redirection can be performed based on the routing information.
created Initialize dependency injection-related operations that iterate over options passed to Methods and initialize option data from
o p t i o n s Get data option ( v m . Options Gets the data options (VM).
Options. data), add an ‘observer’ object to the data and create an observer, define getter, setter storage properties. Called after the instance is created, this stage can access data using Watcher, Events, methods, that is, data Observer and Event/Watcher event configurations are complete. But the DOM has not been mounted yet. This phase allows HTTP request operations to be performed
beforeMount The related Render function is called for the first time
mounted Called after the mount is complete, the render function is executed to generate the virtual DOM, create the real DOM to replace the virtual DOM, and mount it to the instance. You can manipulate the DOM, such as event listening
beforeUpdate
v m . d a t a After the update, virtual d o m Called before rerendering. This hook can be modified After vm.data is updated, the virtual DOM is called before it is re-rendered. This hook can be modified
Vm. data does not trigger additional flush rendering.
updated Called after the virtual DOM is re-rendered
beforeDestroy Called before the instance is destroyed, which means the instance can still be called at this stage.
destroyed Called after the instance has been destroyed. All event listeners have been removed and the subinstance has been destroyed.

7,Life cycle load order of parent and child components

/ / child component
Vue.component('child', {
	template: '<h1>child</h1>'.props: ['message'].beforeCreate() {
		console.log('I am child beforeCreated');
	},
	created() {
		console.log('I am child created');
	},
	beforeMount() {
		console.log('I am child beforeMount');
	},
	mounted() {
		console.log(this.message); // null
		console.log('I am child mounted'); }});/ / the parent component
new Vue({
	el: '#app'.template: ` 
      
`
.data: { message: null }, beforeCreate() { console.log('I am parents beforeCreated'); }, created() { console.log('I am parents created'); }, beforeMount() { console.log('I am parents beforeMount'); }, mounted() { this.message = 'this is message'; console.log('I am parents mounted'); }});Copy the code

Print logs in their respective hook functions and observe the order of execution. The result is that the parent component is created first, then the child component is created; The child component is mounted first, then the parent component.

"I am parents beforeCreated"
"I am parents created"
"I am parents beforeMount"
"I am child beforeCreated"
"I am child created"
"I am child beforeMount"
null
"I am child mounted"
"I am parents mounted"
Copy the code

You can see that null is printed in the child mouted; After the child component is mounted, the parent component is not mounted. Therefore, when component data is displayed, data in the parent component mounted cannot be retrieved from the child component mounted.

Loading the rendering process:

Parent beforeCreate-> Parent created-> parent beforeMount-> child beforeCreate-> child created-> child beforeMount-> Child Mounted -> parent Mounted

Update process:

Parent beforeUpdate-> Child beforeUpdate-> Child updated-> Parent updated

Destruction process:

Parent beforeDestroy-> Child beforeDestroy-> Child destroyed-> Parent destroyed

The life cycle loading sequence of parent and child components can also be understood according to the life cycle flow chart. First, the parent component can be created before the child component can be created. The child component is in the parent component, and the parent component can be fully mounted only after the update is completed.

Eight,To optimize the

(1) User-defined events, timer and other tasks are destroyed in time (can be in beforeDestory) to avoid inner layer leakage;

There are two ways to clean up the timer:

Method 1: the timer method or life cycle function declared and destroyed

  1. First define the timer name in the vue instance’s data:

    export default { data() { timer: null; }};Copy the code
  1. Use a timer when methods or page initialization (Mounted () occurs

    This. timer = setInterval(() => {// need to do things}, 1000);Copy the code
  1. The timer is then destroyed in the page destruction lifecycle function (beforeDestroy())

    export default { data() { timer: null; }, beforeDestroy() { clearInterval(this.timer); this.timer = null; }};Copy the code

$once(‘ hook:beforeDestory ‘,()=>{}); Declare and destroy the timer directly in the method or lifecycle function that requires it

Export default {methods: {fun1() {const timer = setInterval(() => {// need to do things}, 1000); this.$once('hook:beforeDestroy', () => { clearInterval(timer); timer = null; }); }}};Copy the code

Problems of method 1: (1) The vUE instance needs to have an instance of this timer, which feels a bit redundant;

(2) Create timer code and destroy timer code is not put together, usually it is easy to forget to clean the timer, not easy to maintain;

Therefore, method two is recommended

(2) Parent-child component requests are asynchronous

When a parent component calls an interface to pass data to a child component, the request interface response is asynchronous. Which hook in the parent sends the request and which hook in the child receives the data. You might not get it. When all child components are mounted, the parent component may return data. Causes the data passed from the parent to the child to be undefined.

Solution 1:

<div class="test">
	<children v-if="data1" :data="data1"></children>
</div>
Copy the code

When rendering a child component, add a condition that data1 is the data returned by the parent component calling the interface. Render child components when data is available. This creates a natural blockage. Created or Mounted is executed only after the created request of the parent component returns data. Finally, execute mounted of the parent component.

Solution 2:

In the child component watch, the parent component gets the value, the value will change, naturally can listen

watch:{

  data: {deep:true.handler:function(newVal,oldVal) {
    this.$nextTick(() = > {

     this.data = newVal

     this.data = newVal.url ? newVal.url : ' '}})}},Copy the code

Click to call the interface from the parent and display the child, which gets the data and listens to call the method in Watch and display it

props:['data1'].watch: {data1: {deep:true.handler:function(newVal,oldVal) {

    this.$nextTick(() = > {

     this.data1 = newVal

     this.showData1(this.data1)

    })

   }

  },

}
Copy the code

(3) Avoid changing the status after the updated, as this may result in an infinite update loop.

Call after the virtual DOM is re-rendered. If you modify $vm.data again, beforeUpdate and updated will be triggered again, and an endless loop will be entered.

(4) V-if and V-show

Both directives can hide or show elements. When the incoming data is true, it is displayed, false is hidden.

The difference is that v-if removes the element or component (without rendering) from the DOM; V-show uses the display property of the CSS and sets it to None. V-if is true conditional rendering because it ensures that event listeners and subcomponents within the conditional block are properly destroyed and rebuilt during the switch; Also lazy: if the condition is false during the initial render, nothing is done — the conditional block does not start rendering until the condition is true for the first time.

V-if is suitable for scenarios where conditions are rarely changed at runtime and do not need to be switched frequently; V-show is suitable for scenarios that require very frequent switching of conditions.

(5) The importance of KEY

When using V-for to update a list of rendered elements, the default is in-place reuse. When the list data is modified, it will determine whether a value is changed based on the key value. If it is changed, it will re-render the item. Otherwise, it will reuse the previous element.

Use key in loops, and preferably not Index or Random. In diff algorithm, tag and key are used to determine whether the sameNode (sameNode) is used. Using key can reduce the number of rendering times and improve rendering performance.


Why not use index or random as key

During the diff algorithm, the new and old nodes are compared to see if they are of the same type.

If the rendered list is changed, and the index is used as the key, how to change the order of the array, index is 0, 1, 2, and so on, when comparing the old node with the new one, it will be re-rendered. Similarly, random random number as key cannot be found the same in comparison, so it cannot be reused and re-rendered.