React Advanced interview questions: Why are asynchronous requests for data more appropriate in the didMount phase? Does Vue, the leader of MVVM, have a similar problem? On the other hand, I have noticed that the life cycle phase that each person chooses to request data asynchronously is always different, which phase is more suitable for asynchronous request data? What are the implications in terms of product design and user experience? This record addresses both of these issues.

Vue life cycle

Let’s start with a recap of the Vue life cycle and what happens at each stage.

1. BeforeCreated: generates the $options option and adds life-cycle attributes 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, $route, so this stage can do things like redirect based on the routing information.

2. Created: Initialize dependency injection operations that iterate over options passed to methods, initialize option data, get data options from $options (vm.$options.data), add an ‘observer’ object to the data and create an observer, and define getter and 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.

3. BeforeMount: Parsing HTML to generate AST nodes, and then dynamically generating rendering functions based on AST nodes. The related render function is called for the first time (highlighted).

Mounted: After the virtual DOM is mounted, run the render function to generate the virtual DOM, create a real DOM, and mount the virtual DOM to the instance. You can manipulate the DOM, such as event listening

BeforeUpdate: $vm.data is called after the update and before the virtual DOM is re-rendered. $vm.data can be modified in this hook without triggering additional flush rendering.

6. Updated: Invoked after the virtual DOM is rerendered. If $vm. Data is modified again, beforeUpdate and updated will be triggered again to enter an endless loop.

BeforeDestroy: called before the instance is destroyed, meaning the instance can still be called at this stage.

8. Destroyed: Called after the instance is destroyed. All event listeners are removed and the subinstance is destroyed.

In summary, the virtual DOM starts rendering in the beforeMount phase, and the DOM instance is displayed in the Mounted phase after it is mounted.

So the next thing to know is the render function.

Render the sample:export default {
   data () {
       return{menu_items: [] // Request return such as: [{fullname:'Page one'},{fullname: 'Page 2'},{fullname: 'Page 3'},{fullname: 'Page 4'}}, render (createElement){returnCreateElement (// 1. The first argument, the name of the tag to render (mandatory)'ul', // 2. The second argument, the attribute of the tag to render in 1, or the text element (optional){class: {'uk-nav': true},}, // 3. This. Menu_items. Map (item=>createElement())'li', item. Fullname}}))))Copy the code

The render function finally returns the createNodeDescription, commonly known as a Virtual node. Template would look like this:

< the template > < ul > < li v - for ="item in menu_items"> {{item.fullname}} </li></ul> </template>Copy the code

This process is completed before Mounted is called. Details can be found here

Two, asynchronous loading

Asynchronous functions such as setTimeout

The main difference between asynchronous and synchronous functions is that asynchronous functions wait until all synchronous functions have been executed. See the event loop for details.

// num created:function () {
    console.group('created Created state =============== ')
    console.log('%c%s'.'color:red'.'el : ' + this.$el) // undefined
    console.log('%c%s'.'color:red'.'data : ' + this.$data// Console. log(console.log) has been initialized.'%c%s'.'color:red'.'message: '+ this.message) // has been initialized // new code snippetsetTimeout(() => {// If this is a normal function, please note that this pointer is not used in vue. This. Num ++ this. Num += 2}, 0setTimeout(() => {
      this.num -= 5
    }, 0)
 }Copy the code

Console promises results:



(Slightly distorted… The screenshot is too big and slightly compressed!! -)

When vue executes code, it doesn’t care what happens in the timer, and even after setting a 0 delay, it still executes other life cycles sequentially, seemingly skipping the asynchronous load. So you can be sure that asynchronous operations in the lifecycle do not execute sequentially, but wait until the non-asynchronous operations have finished. Therefore, when writing this part of the code, please note that the logic in this part is not tied to the order. Make sure that any asynchronous operation, even if it is executed last, does not throw an exception that blocks the entire process.

Ajax asynchronous request

Ajax requests are asynchronous operations, and the execution time of callback functions is uncertain. That is, even after the CREATED hook sends a request, the DOM is mounted and the request does not return a result, which could result in a runtime error such as:



Because the menu_items in the Render example above are still empty.

The solution

In the case of ajax asynchronous requests, the reason for this error is that the return result does not catch up with the DOM node rendering. So you can change it in two ways: on the assignment variable that returns the result, and on the rendering level of the DOM node.

1. Give the assignment variable its initial value, i.e., menu_items: [{fullname: ‘}] at definition.

The advantage of this is that the render of the page node is not limited to the return result, the static text is rendered and the dynamic data is populated as the data is updated. The feeling to the user is that the page rendering speed is good.

However, this approach also has drawbacks, the background return data field is not the same, if all write that is really troublesome.

Of course you don’t have to worry about this if you use typescript. Menu_items: {[propName: string]: any} = {} takes care of that.

2. V-if, controls the mount of the DOM node and starts rendering the node only when menu_items is given a return value.

The advantage of this is that static and dynamic copywriting are presented to users simultaneously, without copywriting jumping and data coming from nothing. However, the side effect is longer page rendering time and user waiting time.

What if the dom request data was returned before it was mounted?

We can simulate this with setTimeout

<span>{{person.name.firstName}}</span> data: function () { return { message: 'hello world', add: 1, person: { name: {}}}}, created: the function () {console. Group (' created is created state = = = = = = = = = = = = = = = "") to the console. The log (% c '% s',' color: red, 'el: ' + this.$el) // undefined console.log('%c%s', 'color:red', 'data : // console.log('%c%s', 'color:red', 'message: SetTimeout (() => {this.person = {name: {lastName: 'carry', firstName: 'dong'}, sex: 'male'}, 0)Copy the code

Request early enough, but still error, enclosing person. Name. FirstName is undefined, but programs offer after the fault or to continue.

Third, the conclusion

Since asynchronous functions do not block the entire VUE lifecycle, requests can be made at any stage. If you want the user to feel that the page is loaded this morning and reduce the amount of blank page time, it is recommended to put it in the creation phase and then deal with null and undefined. After all, the earlier the data is retrieved, the more timely the mounted instance will be rendered.

Of course, even in this case, the updated life hook (data has a default value and is rendered, after which the data is updated) is triggered, resulting in a re-rendering of the virtual DOM.