Why Vue uses asynchronous rendering

itread01 2021-02-23 00:39:55
vue uses asynchronous rendering


# Vue Why asynchronous rendering `Vue` Updating `DOM` Time is asynchronous , Just listen for data changes ,`Vue` A queue will be opened , And buffer all data changes in the same event loop , If the same `watcher` Triggered multiple times , Will only be pushed into the queue once , This method of removing duplicate data in buffering is good for avoiding unnecessary computation and `DOM` Operation is very important , And then , Loop back in the next event `tick` in ,`Vue` Reorganize the queue and execute the actual ( It's gone heavy ) Work ,`Vue` Attempt to use native... For asynchronous queues internally `Promise.then`、`MutationObserver` and `setImmediate`, If the execution environment does not support , Will use `setTimeout(fn, 0)` Instead of .## Description for `Vue` Why asynchronous rendering , In short, it's to improve efficiency , Because asynchronous updates are not used , The current component will be re rendered every time the data is updated , For efficiency ,`Vue` After this round of data update , Go to asynchronous update view , For example , Let's update a value repeatedly within a method .```javascriptthis.msg = 1;this.msg = 2;this.msg = 3;``` In fact , What we really want is just the last update , That is to say, the first three times `DOM` Updates can be omitted , We just need to wait for all the States to be changed before rendering, which can reduce some performance loss . The problem with rendering is very clear , Rendering only once in the end is definitely less efficient than rendering after modification , Here we also need to consider the issues related to asynchronous update queues , Let's assume that we have processed the data so that the data is updated only once `DOM` Rendering , Let's consider the performance optimization of asynchronous update queue . Let's say this is a synchronous update queue ,`this.msg=1`, It's about to happen : `msg` Value update `->` Trigger `setter` `->` Trigger `Watcher` Of `update` `->` Call again `render` `->` Generate a new `vdom -> dom-diff -> dom` to update , Here `dom` Updating is not rendering ( It's the layout 、 Draw 、 Synthesis and so on ), It's updating the memory `DOM` Tree structure , We'll do it later `this.msg=2`, Repeat the above steps , The second time after that `3` Each update triggers the same process , When you start rendering , Abreast of the times `DOM` It's true that only update completion exists in the tree `3`, From here on , front `2` Second right `msg` The operation of and `Vue` Internal processing of it is useless operation , It can be optimized . If it is an asynchronous update queue , It will be the following , Execute `this.msg=1`, Not immediately , It's going to be about `msg` There are dependent `Watcher` Are stored in the queue , The queue might look like this `[Watcher1, Watcher2...]`, When executed `this.msg=2` After , The same will be true for `msg` There are dependent `Watcher` Save to queue ,`Vue` The interior will do to re judge , After this operation , It can be assumed that the queue data has not changed , The first `3` The next update is also the above process , Of course , You can't just be right `msg` There are operations , You may also have an operation on another attribute in the component , such as `this.otherMsg=othermessage`, The same will be true of `otherMsg` There are dependent `Watcher` Added to asynchronous update queue , Because there's repetitive judgment , This `Watcher` It will only exist once in the queue , After the execution of this asynchronous task , It will enter the next task execution process , It's actually traversing every one of the asynchronous update queues `Watcher`, Trigger it `update`, Then make a new call `render` `->` `new vdom` `->` `dom-diff` `->` `dom` Update and so on , But compared with synchronous update queues , No matter how many times `msg`,` Vue` Internally, there is only one call to the real update process , therefore , For asynchronous update queue, it does not save rendering cost , It saves `Vue` Internal calculation and `DOM` The cost of tree operations , Either way , Rendering really only once . Besides , Actual use inside the component `VirtualDOM` Render , That is to say , Inside the component, it doesn't care which state has changed , It only needs to calculate once to know which nodes need to be updated , That is to say , If changed `N` A state , In fact, you only need to send a signal to `DOM` Update to the latest , If we update multiple values . ```javascriptthis.msg = 1;this.age = 2;this.name = 3;``` Here we modify three states three times , But in fact `Vue` It will only render once , Because `VIrtualDOM` It only takes one time for the entire component to be `DOM` Update to the latest , It doesn't care which specific state the update signal is sent from . And to do that , We need to postpone rendering until all States are modified , To do this, you just need to postpone the rendering until the end of this event loop or the next event loop , That is to say , Just at the end of this cycle , After all the previous statements to update the status are executed , Perform a render operation , It can ignore the previous syntax of various update States , No matter how many statements are written to update the status , Just one final rendering . It's much faster to postpone rendering to the end of this event loop than to the next one , therefore `Vue` The priority is to postpone the rendering operation to the end of the current event loop , If the execution environment does not support it, it will be degraded to the next round ,`Vue` The mechanism of change detection (`setter`) It will send out the signal of rendering every time the state changes , but `Vue` After receiving the signal, it will check whether the task already exists in the queue , Make sure there are no duplicates in the queue , If the queue does not exist, add the render operation to the queue , After that, all rendering operations in the queue are delayed and the queue is emptied by asynchronous method , When the same round of events repeatedly modify the state in the loop , It doesn't add the same rendering operation to the queue over and over again , So we're using `Vue` When , Update after modifying the status `DOM` It's all asynchronous . When the data changes, it will call `notify` Method , Will `watcher` Traversal , call `update` Method notice `watcher` Update , Now `watcher` Not immediately , stay `update` Middle meeting call `queueWatcher` Method will `watcher` Put it in a queue , stay `queueWatcher` According to `watcher` To carry out the de duplication of , If multiple attributes depend on one `watcher`, If the queue does not have the `watcher` It will be `watcher` Add to queue , And then it will be in `$nextTick` Method is added to the execution queue of the `flushSchedulerQueue` Method ( This method will trigger the execution of all callbacks in the buffer queue ), Then the `$nextTick` Method call back to add `$nextTick` The execution queue maintained in method ,`flushSchedulerQueue` In the beginning, a `before` Methods , It's just `beforeUpdate`, And then `watcher.run`() It's just beginning to really execute `watcher`, After executing the page, the rendering is complete , When the update is complete, it will call `updated` Hooks .## $nextTick In the above, we talked about `Vue` Why asynchronous rendering , If we have a need at this time , You need to get the page's `DOM` Elements , And because rendering is asynchronous , We can't get this value synchronously in the defined method , So there is `vm.$nextTick` Method ,`Vue` in `$nextTick` Method to delay the callback until the next time `DOM` Execute after update loop , That is, next time `DOM` After the end of the update loop, a delayed callback is executed , Use this method immediately after modifying the data , Can get the updated `DOM`. In short, when the data is updated , stay `DOM` After the rendering is finished , Execute callback function . A simple example is given to demonstrate `$nextTick` The role of methods , The first thing you need to know is `Vue` Updating `DOM` Time is asynchronous , In other words, it does not block code execution when updating data , Until the end of code execution in the execution stack , To start executing code for asynchronous task queues , So when the data is updated , Components don't render immediately , At this point in the acquisition of `DOM` The value obtained after the structure is still the old value , And in the `$nextTick` The callback function set in the method is executed after the component rendering is completed , obtain `DOM` The value obtained after the structure is the new value .```html Vue
```### The asynchronous mechanism is described in the official document ,`Vue` Updating `DOM` Time is asynchronous , Just listen for data changes ,`Vue` A queue will be opened , And buffer all data changes in the same event loop , If the same `watcher` Triggered multiple times , Will only be pushed into the queue once . This method of removing duplicate data in buffering is good for avoiding unnecessary computation and `DOM` Operation is very important . And then , Loop back in the next event `tick` in ,`Vue` Rearrange the queue and perform the actual work .`Vue` Attempt to use native... For asynchronous queues internally `Promise.then`、`MutationObserver` and `setImmediate`, If the execution environment does not support , Will use `setTimeout(fn, 0)` Instead of . `Js` It's single threaded , It introduces synchronous blocking and asynchronous non blocking execution modes , stay `Js` An... Is maintained in asynchronous mode `Event Loop`,`Event Loop` It's an execution model , There are different realizations in different places , Browser and `NodeJS` Based on different technologies, they have realized their own `Event Loop`. Browser `Event Loop` Is in `HTML5` It is clearly defined in the specification of ,`NodeJS` Of `Event Loop` Based on `libuv` Realized . In the browser `Event Loop` By the execution stack `Execution Stack`、 Background thread `Background Threads`、 The megaphone queue `Macrotask Queue`、 Micro queue `Microtask Queue` Make up . * The execution stack is the data structure of the synchronization task in the main thread , Function calls form a stack of frames . * The background thread is the browser implementation for `setTimeout`、`setInterval`、`XMLHttpRequest` And so on .* The megaphone queue , Calls back from some asynchronous tasks go into the macro queue in turn , Waiting for subsequent calls , Include `setTimeout`、`setInterval`、`setImmediate(Node)`、`requestAnimationFrame`、`UI rendering`、`I/O` Wait for the operation .* Micro queue , Calls back from other asynchronous tasks will enter the micro queue in turn , Waiting for subsequent calls , Include `Promise`、`process.nextTick(Node)`、`Object.observe`、`MutationObserver` Wait for the operation . When `Js` On execution , Proceed as follows : 1. First, run the code in the execution stack synchronously , Add the asynchronous tasks in the code to the background thread .2. When the synchronization code in the execution stack is finished , Perform stack emptying , And start scanning micro queues .3. Take out the micro queue first task , Put it in the execution stack to execute , At this time, the micro queue is out of the queue .4. When the execution stack is finished , Continue to queue tasks and execute , Until the micro Queue task is completed .5. After the last micro Queue task is queued and enters the execution stack, the task in the micro queue is empty , When the execution stack task is completed , Start scanning micro queue is empty , Continue scanning the macro queue task , The megaphone line up , Put it in the execution stack to execute , After execution, continue to scan the macro queue if the micro queue is empty , Out of line execution .6. Keep going back and forth `...`.#### For example ```javascript// Step 1console.log(1);// Step 2setTimeout(() => { console.log(2); Promise.resolve().then(() => { console.log(3); });}, 0);// Step 3new Promise((resolve, reject) => { console.log(4); resolve();}).then(() => { console.log(5);})// Step 4setTimeout(() => { console.log(6);}, 0);// Step 5console.log(7);// Step N// ...// Result/* 1 4 7 5 2 3 6*/```##### Step 1```javascript// Execution stack console// Micro queue []// The megaphone queue []console.log(1); // 1```##### Step 2```javascript// Execution stack setTimeout// Micro queue []// The megaphone queue [setTimeout1]setTimeout(() => { console.log(2); Promise.resolve().then(() => { console.log(3); });}, 0);```##### Step 3```javascript// Execution stack Promise// Micro queue [then1]// The megaphone queue [setTimeout1]new Promise((resolve, reject) => { console.log(4); // 4 // Promise It's a function object , This is the synchronous execution // Execution stack Promise console resolve();}).then(() => { console.log(5);})```##### Step 4```javascript// Execution stack setTimeout// Micro queue [then1]// The megaphone queue [setTimeout1 setTimeout2]setTimeout(() => { console.log(6);}, 0);```##### Step 5```javascript// Execution stack console// Micro queue [then1]// The megaphone queue [setTimeout1 setTimeout2]console.log(7); // 7```##### Step 6```javascript// Execution stack then1// Micro queue []// The megaphone queue [setTimeout1 setTimeout2]console.log(5); // 5```##### Step 7```javascript// Execution stack setTimeout1// Micro queue [then2]// The megaphone queue [setTimeout2]console.log(2); // 2Promise.resolve().then(() => { console.log(3);});```##### Step 8```javascript// Execution stack then2// Micro queue []// The megaphone queue [setTimeout2]console.log(3); // 3```##### Step 9```javascript// Execution stack setTimeout2// Micro queue []// The megaphone queue []console.log(6); // 6```### After understanding the execution queue of asynchronous tasks , Back in the middle `$nextTick` Method , When the user profile is updated ,`Vue` A buffer queue will be maintained , For all updated data, the component rendering will be done with `DOM` The buffer queue is added after certain policy processing , And then it will be in `$nextTick` Method is added to the execution queue of the `flushSchedulerQueue` Method ( This method will trigger the execution of all callbacks in the buffer queue ), Then the `$nextTick` Method call back to add `$nextTick` The execution queue maintained in method , When the execution queue of an asynchronous mount is triggered, it will execute first `flushSchedulerQueue` How to deal with `DOM` The task of rendering , And then go to the execution `$nextTick` Method building tasks , In this way, it can be realized in `$nextTick` Method to get the rendered `DOM` Structure . In the process of testing, we found an interesting phenomenon , In the example above, add two buttons , Click to select `updateMsg` The result of the button is `3 2 1`, Click on `updateMsgTest` The execution result of the button is `2 3 1`.```html Vue
``` This assumes that in the execution environment `Promise` Objects are fully supported , Then use `setTimeout` The macro queue is executed at the end. There is no objection to this , But use `$nextTick` Methods and self defined `Promise` The example item has the problem of execution order , Although they are all micro queue tasks , But in `Vue` Because of the specific implementation in, the execution order may be different , First, take a look at `$nextTick` Method's source code , Key points added comments , Please note that this is `Vue2.4.2` The original code of the version , In the later period `$nextTick` The method may change .```javascript/** * Defer a task to execute it asynchronously. */var nextTick = (function () { // Closure Internal variables var callbacks = []; // Execution queue var pending = false; // Identification , Add a circle to an event to determine whether it is the first time , The asynchronous queue mount is triggered only when the first join is made var timerFunc; // How to mount the asynchronous execution queue , Let's assume that Promise It's fully supported function nextTickHandler () { // Asynchronously mounted execution tasks , When triggered, it is officially ready to start the asynchronous task pending = false; // Logo location false var copies = callbacks.slice(0); // Make a copy callbacks.length = 0; // The execution queue is empty for (var i = 0; i < copies.length; i++) { copies[i](); // Execute } } // the nextTick behavior leverages the microtask queue, which can be accessed // via either native Promise.then or MutationObserver. // MutationObserver has wider support, however it is seriously bugged in // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It // completely stops working after triggering a few times... so, if native // Promise is available, we will use it: /* istanbul ignore if */ if (typeof Promise !== 'undefined' && isNative(Promise)) { var p = Promise.resolve(); var logError = function (err) { console.error(err); }; timerFunc = function () { p.then(nextTickHandler).catch(logError); // Mount asynchronous task queue // in problematic UIWebViews, Promise.then doesn't completely break, but // it can get stuck in a weird state where callbacks are pushed into the // microtask queue but the queue isn't being flushed, until the browser // needs to do some other work, e.g. handle a timer. Therefore we can // "force" the microtask queue to be flushed by adding an empty timer. if (isIOS) { setTimeout(noop); } }; } else if (typeof MutationObserver !== 'undefined' && ( isNative(MutationObserver) || // PhantomJS and iOS 7.x MutationObserver.toString() === '[object MutationObserverConstructor]' )) { // use MutationObserver where native Promise is not available, // e.g. PhantomJS IE11, iOS7, Android 4.4 var counter = 1; var observer = new MutationObserver(nextTickHandler); var textNode = document.createTextNode(String(counter)); observer.observe(textNode, { characterData: true }); timerFunc = function () { counter = (counter + 1) % 2; textNode.data = String(counter); }; } else { // fallback to setTimeout /* istanbul ignore next */ timerFunc = function () { setTimeout(nextTickHandler, 0); }; } return function queueNextTick (cb, ctx) { // nextTick Methods the method of real export var _resolve; callbacks.push(function () { // Add to execution queue And add exception handling if (cb) { try { cb.call(ctx); } catch (e) { handleError(e, ctx, 'nextTick'); } } else if (_resolve) { _resolve(ctx); } }); // Determine whether it is the first time to add... In the current event loop , If it is added for the first time, set the label as true And execute timerFunc Function to mount the execution queue to Promise // This ID is set to when the task in the execution queue is about to be executed false And create a copy of the execution queue to perform tasks in the execution queue , See nextTickHandler The realization of function // Put a marker in the current event loop true And mount it , Then call again nextTick Method only adds the task to the execution queue , Until the mounted asynchronous task triggers , The logo is set as false And then carry out the task , Call again nextTick Methods are executed in the same way, and then repeatedly if (!pending) { pending = true; timerFunc(); } if (!cb && typeof Promise !== 'undefined') { return new Promise(function (resolve, reject) { _resolve = resolve; }) } }})();``` Back to the question just raised , Updating `DOM` Operation will trigger first `$nextTick` Method callback , The key to solve this problem is who first mounts the asynchronous task to `Promise` On the object . First of all, for those with updated data `updateMsg` Button trigger method `debug`, The breakpoint is set at `Vue.js` Of `715` That's ok , The version is `2.4.2`, When you look at the call stack and the arguments passed in, you can see the first execution `$nextTick` Method is called due to data update `nextTick(flushSchedulerQueue);` Sentence , In other words, it's executing `this.msg = "Update";` It's already triggered the first time `$nextTick` Method , At this time in the `$nextTick` The task queue in method will first `flushSchedulerQueue` Method to queue and mount `$nextTick` The execution of the method is queued to `Promise` On the object , And then it's self defining `Promise.resolve().then(() => console.log(2))` Mounting statements , When executing a task in a micro task queue , The first mount to is performed first `Promise` The task of , At this point, the task is to execute the execution queue , There are two methods in this queue , First of all, it will execute `flushSchedulerQueue` Method to trigger the element `DOM` Rendering operations , And then execute `console.log(3)`, And then execute the task of the second micro queue, that is `() => console.log(2)`, At this point, the micro task queue is empty , Then go to the macro task queue to execute `console.log(1)`. Next, for those with no data to update `updateMsgTest` Button trigger method `debug`, The breakpoints are set at the same location , There is no update at this time , So the first trigger `$nextTick` Method is a self-defined callback function , Then at this time `$nextTick` The execution queue of the method will be mounted to `Promise` On the object , Obviously, the self-defined output before that `2` Of `Promise` Callback has been mounted , Then the execution process of the method bound by this button is executed first `console.log(2)`, Then execute `$nextTick` The execution queue of the method closure , At this point, there is only one callback function in the execution queue `console.log(3)`, At this point, the micro task queue is empty , Then go to the macro task queue to execute `console.log(1)`. In short, it means who will mount first `Promise` The problem with objects , Calling `$nextTick` Method will mount the execution queue maintained internally by its closure to `Promise` thing , When the data is updated `Vue` Internally, it will be implemented first `$nextTick` Method , After that, the execution queue is mounted to `Promise` On the object , In fact, I understand `Js` Of `Event Loop` After model , Think of data updates as a `$nextTick` Method call , And understand `$nextTick` Method performs all push callback at once , We can understand the problem of execution order , The following is about `$nextTick` Methods of minimization `DEMO`.```javascriptvar nextTick = (function(){ var pending = false; const callback = []; var p = Promise.resolve(); var handler = function(){ pending = true; callback.forEach(fn => fn()); } var timerFunc = function(){ p.then(handler); } return function queueNextTick(fn){ callback.push(() => fn()); if(!pending){ pending = true; timerFunc(); } }})();(function(){ nextTick(() => console.log(" Trigger DOM How to render queues ")); // Note / Uncomment To see the effect setTimeout(() => console.log(1)) Promise.resolve().then(() => console.log(2)) nextTick(() => { console.log(3) })})();```## One question a day ```https://github.com/WindrunnerMax/EveryDay```## Refer to ```https://zhuanlan.zhihu.com/p/29631893https://github.com/berwin/Blog/issues/22https://juejin.cn/post/6899822303022956552https://segmentfault.com/a/1190000015698196https://cn.vuejs.org/v2/guide/reactivity.htmlhttps://blog.csdn.net/weixin_46396187/article/details/10746
版权声明
本文为[itread01]所创,转载请带上原文链接,感谢
https://qdmana.com/2021/02/20210222171200340K.html

  1. 【微前端】微前端最终章-qiankun指南以及微前端整体探索
  2. Vue-Cli 创建vue3项目
  3. Go in the front of the progress of u boot v7.0 U disk boot disk production tools
  4. 使用NTLM的windows身份验证的nginx反向代理
  5. Rust教程:针对JavaScript开发人员的Rust简介
  6. 使用 Serverless Framework 部署个人博客到腾讯云
  7. #研發解決方案#易車前端監控系統
  8. Vue changes localhost to IP address and cannot access
  9. JavaScript进阶学习
  10. HTML5 from entry to proficient, realize annual salary 10W +, zero basic students must see
  11. Vue:vuex状态数据持久化插件vuex-persistedstate
  12. Vue source code analysis - start
  13. Vue -- the child component calls the method of the parent component and passes parameters --- props
  14. React-Native 获取设备当前网络状态 NetInfo
  15. 高性能 Nginx HTTPS 调优 - 如何为 HTTPS 提速 30%
  16. How to learn HTML5? How can Xiaobai start HTML5 quickly?
  17. HTML + CSS detailed tutorial, this article is enough, but also quickly save
  18. JavaScript高级:JavaScript面向对象,JavaScript内置对象,JavaScript BOM,JavaScript封装
  19. Why Vue uses asynchronous rendering
  20. JavaScript高级:JavaScript面向对象,JavaScript内置对象,JavaScript BOM,JavaScript封装
  21. vue判断elementui中el-form是否更新变化,变化就提示是否保存,没变就直接离开
  22. 算法题:两数之和——JavaScript及Java实现
  23. 高性能 Nginx HTTPS 调优
  24. Why Vue uses asynchronous rendering
  25. day 31 jQuery进阶
  26. day 30 jQuery
  27. CSS whimsy -- using background to create all kinds of wonderful backgrounds
  28. Why are more and more people learning front end?
  29. What do you do with 4K front-end development?
  30. 8 years of front-end development knowledge precipitation (do not know how many words, keep writing it...)
  31. What is the annual salary of a good web front end?
  32. Front end novice tutorial! How to get started with web front end
  33. Will the front end have a future?
  34. Is the front end hard to learn?
  35. Seven new Vue combat skills to improve efficiency in 2021!
  36. Is front end learning difficult?
  37. How about the process of Web front-end development and self-study?
  38. Front end learning route from zero basis to proficient
  39. What is the basis of learning front end?
  40. What knowledge points need to be learned for self-study front end? How long can I become a front-end Engineer?
  41. An inexperienced front-end engineer, what are the common problems when writing CSS?
  42. HttpServletRequest get URL (parameter, path, port number, protocol, etc.) details
  43. Springboot starts http2
  44. Enabling http2.0 in spring boot
  45. JQuery:JQuery基本语法,JQuery选择器,JQuery DOM,综合案例 复选框,综合案例 随机图片
  46. Using JavaScript in Safari browser history.back () the page will not refresh after returning to the previous page
  47. vue.js Error in win10 NPM install
  48. In less than two months, musk made more than $1 billion, more than Tesla's annual profit
  49. Springboot starts http2
  50. Vue event bus
  51. JQuery easy UI tutorial: custom data grid Pagination
  52. Using okhttp and okhttpgo to obtain onenet cloud platform data
  53. Vue3 component (IX) Vue + element plus + JSON = dynamic rendering form control
  54. HTTP 1. X learning notes: an authoritative guide to Web Performance
  55. Vue3 component (IX) Vue + element plus + JSON = dynamic rendering form control
  56. HTTP 1. X learning notes: an authoritative guide to Web Performance
  57. JQuery:JQuery基本语法,JQuery选择器,JQuery DOM,综合案例 复选框,综合案例 随机图片
  58. Event bubble and capture in JavaScript
  59. The root element is missing solution
  60. Event bubble and capture in JavaScript