In vUE source code learning 11: Responsive principles and dependency collection article, the use of Watcher classes and De classes to achieve data and view responsiveness, and understand how vUE is dependent collection.
This leaves one problem: if you update a Watcher frequently, the page will render frequently.
In the vUE source code, is the use of an asynchronous mechanism for page updates. Today we are going to learn the principle of asynchronous update
Performance issues to solve
There is an update method in Watcher that is executed multiple times as the data is frequently updated.
update() {
// This method will operate frequently
this.get()
}
get() {
// What get does is render the page
pushTarget(this)
this.getter()
popTarget()
}
Copy the code
We know that the this.getter method calls the following method
updateComponent = () = > {
// 1. Generate the virtual DOM with render
vm._update(vm._render()) // Subsequent updates can invoke the updateComponent method
// 2. Virtual Dom generates real Dom
}
Copy the code
The result is very poor performance. To improve efficiency, Vue did two things:
- Cache watcher every time you update it
- If multiple times with the new is a Watcher, merge it into one and render the page together
queueWatcher
QueueWatcher As the name implies, this is a watcer queue.
We created a scheduler.js in the Observer folder
Scheduler is a JS file about a scheduler
// Schedule work scheduler.js
import { nextTick } from '.. /util';
// 1. Remove weight 2
let queue = []
let has = {} // The list maintains which watcher is stored
function flushSchedulerQueue() {
for (let i = 0; i < queue.length; i++) {
queue[i].run()
}
queue = []
has = {}
pending = false
}
let pending = false
export function queueWatcher(watcher) {
// Multiple updates will receive multiple watcher
const id = watcher.id
if (has[id] == null) {
queue.push(watcher)
has[id] = true
// Start batch update operation (anti-shake)
if(! pending) { nextTick(flushSchedulerQueue,0)
pending = true}}}Copy the code
The related variable methods in this file scheduler.js function as follows:
- Queue: A queue for the watcher to be changed
- Has: this is used to store the id of watcher. The id value is used to remove the duplicate. If the ID exists in this has object, it is no longer wanted to store in queue, otherwise it would be stored in queue
- FlushSchedulerQueue: flushes the scheduling queue to render all watchers in the queue. At the same time, reset the parameters related to scheduling, including
queue
,has
,pending
- Pending: In rendering, this is a lock-like concept. If pending is false, start a batch operation until all watchers have been executed, and the pending state is reset
NextTick method
A nextTick method is used in scheduler, as shown below
const callbacks = []
function flushCallbacks() {
callbacks.forEach(cb= > cb())
waiting = false
}
function timer(flushCallbacks) {
let timerFn = () = >{}if (Promise) {
timerFn = () = > {
Promise.resolve().then(flushCallbacks)
}
} else if (MutationObserver) {
// This is also a microtask
let textNode = document.createTextNode(1)
let observe = new MutationObserver(flushCallbacks)
observe.observe(textNode, {
characterData: true
})
timerFn = () = > {
textNode.textContent = 3}}else if (setImmediate) {
timerFn = () = > {
setImmediate(flushCallbacks)
}
} else {
timerFn = () = > {
setTimeout(flushCallbacks, 0)
}
}
timerFn()
}
export function nextTick(cb) {
callbacks.push(cb) $nextTick($nextTick); $nextTick($nextTick);
if(! waiting) { timer(flushCallbacks) waiting =true}}Copy the code
A simple way to interpret this code is to use a microtask or macro task to execute a set of Watcher tasks. The code in the Timer method is compatible with the handling of browser microtasks and macro tasks. This process is not compatible with Vue3.0.
Detailed combing is as follows:
A flushSchedulerQueue callback cb is passed into scheduler.js and stored in the callbacks queue.
If the state is not waiting, start a microtask (macro task if incompatible) to execute the callback and then reset the wating state
$nextTick
This.$nextTick(), which is often used in Vue, uses the nextTick method.
That is, a nextTick method is mounted on prototype at vUE initialization.
import { nextTick } from './util';
export function lifecycleMixin(Vue) {
/ /... Other code
Vue.prototype.$nextTick = nextTick
}
Copy the code
Thus, we can call the $nextTick method from an instance of vue.
Well, that’s the end of today’s lesson, and I’m looking forward to learning how to update arrays next time.
Historical articles
- How does CodeGen convert ast syntax trees into Render strings?
- Vue source code learning 9: virtual Dom implementation principle
- Vue source code learning 10: create a virtual DOM into a real DOM
- Vue source code learning 11: Responsive principles and dependency collection