preface
The previous article wrote Vue series computed implements the computed properties of Vue.
The target
This article is the final in a series of handwritten Vue implementations that implement asynchronous update queues for Vue.
Read the source code, I believe we all know the Vue asynchronous update process: After the dependency collection is complete, When reactive data changes -> trigger setter to execute dep. Notify -> let DEP notify all watcher collected by itself to execute update method -> watch. Update calls queueWatcher to put itself in The Watcher queue -> next call to the nextTick method puts the method refreshing the Watcher queue into the Callbacks array -> then puts the method refreshing the Callbacks array into the browser’s asynchronous task queue -> finally fires when it is executed in the future The watcher.run method executes the watcher.get method.
implementation
Next, the Vue asynchronous update queue will be fully implemented to give you a thorough understanding of what happens with Vue asynchronous updates.
Watcher
/src/watcher.js
// To mark watcher
let uid = 0** * @param {*} cb callback function, responsible for updating DOM callback function * @param {*} options watcher configuration items */export default function Watcher(cb, options = {}, vm = null) {
/ / logo watcher
this.uid = uid++
// ...
}
Copy the code
watcher.update
/src/watcher.js
/** * DeP tells Watcher to update the DOM */ using the this._cb function
Watcher.prototype.update = function () {
if (this.options.lazy) { // Lazy execution, such as computed attributes
// With dirty set to true, the evalute method can be used to get the latest values when the page is re-rendered to get the calculated attributes
this.dirty = true
} else {
// Put watcher into the asynchronous Watcher queue
queueWatcher(this)}}Copy the code
watcher.run
/src/watcher.js
/** * is called by the function that refreshes the watcher queue, responsible for executing the watcher.get method */
Watcher.prototype.run = function () {
this.get()
}
Copy the code
Asynchronous update queue
/src/asyncUpdateQueue.js
/** * asynchronously update queue */
// Store all the watcher for this update
const queue = []
// Indicates whether the Watcher queue is now being flushed
let flushing = false
// flag to ensure that there is only one function in the Callbacks array that refreshes the Watcher queue
let waiting = false
// Stores the function to refresh the Watcher queue, or the callback passed by the user calling vue. nextTick
const callbacks = []
// Identifies whether there is a function to refresh the Callbacks array in the browser's current task queue
let pending = false
Copy the code
queueWatcher
/src/asyncUpdateQueue.js
/** * Put watcher in the queue *@param {*} Watcher The watcher that needs to be performed later, including render Watcher, user watcher, computed */
export function queueWatcher(watcher) {
if(! queue.includes(watcher)) {// Prevent duplicate entries
if(! flushing) {// The watcher queue is not being flushed
queue.push(watcher)
} else { // The watcher queue is being flushed, for example, the user Watcher's callback has changed some reactive data
// Flag whether the current watcher is enqueued in for
let flag = false
// The watcher queue is in order (uid increases from small to large), and the current watcher is still in order after being inserted
for (let i = queue.length - 1; i >= 0; i--) {
if (queue[i].uid < watcher.uid) { // Find the location of the watcher that is exactly smaller than the current watcher.uid
// Insert the current watcher after the position
queue.splice(i + 1.0, watcher)
flag = true
break; }}if(! flag) {// The for loop did not find a watcher smaller than the current watcher.uid in the queue
// Insert the current watcher to the head of the queue
queue.unshift(watcher)
}
}
if(! waiting) {// Indicates that there is no function in the current callbacks array to refresh the Watcher queue
// Make sure there is only one function in the callbacks array that flushes the Watcher queue
// Because if there are more than one, it doesn't make any sense, and the watcher queue will be empty by the time the second one is executed
waiting = true
nextTick(flushSchedulerQueue)
}
}
}
Copy the code
flushSchedulerQueue
/src/asyncUpdateQueue.js
/** * Flushes the watcher queue function called */ in flushCallbacks
function flushSchedulerQueue() {
// Indicates that the Watcher queue is being flushed
flushing = true
// Sort the watcher queue from smallest to largest by uid
queue.sort((a, b) = > a.uid - b.uid)
// Traverse the queue, executing each watcher's run method in turn
while (queue.length) {
// Select watcher as the team leader
const watcher = queue.shift()
// Execute the run method
watcher.run()
}
// The watcher queue is refreshed
flushing = waiting = false
}
Copy the code
nextTick
/src/asyncUpdateQueue.js
/** * Place the function to refresh the Watcher queue or the callback passed by the user calling vue. nextTick into the callbacks array * If there is no function to refresh the callbacks in the current browser task queue, Put the flushCallbacks function into the task queue */
function nextTick(cb) {
callbacks.push(cb)
if(! pending) {// Indicates that the browser does not have a function to refresh the Callbacks array in the current task queue
// Put the flushCallbacks function into the browser's microtask queue
Promise.resolve().then(flushCallbacks)
// Indicate that the browser microtask queue already has a function to refresh the Callbacks array
pending = true}}Copy the code
flushCallbacks
/src/asyncUpdateQueue.js
/** * the function responsible for refreshing the callbacks array, executes all the functions in the callbacks array */
function flushCallbacks() {
FlushCallbacks = flushCallbacks = flushCallbacks = flushCallbacks = flushCallbacks
// The new flushCallbacks function can now enter the browser task queue
pending = false
while(callbacks.length) {
// Take out the uppermost callback function
const cb = callbacks.shift()
// Execute the callback function
cb()
}
}
Copy the code
conclusion
This brings us to the end of the Master Vue series, so let’s take a look back at the whole series: 20 articles from the beginning of the Vue source code interpretation to the current handwritten Vue. If you follow from beginning to end, we believe that the original set of goals have been achieved, now you can write on your resume: proficient in Vue source code principles.
Focus on
Welcome everyone to pay attention to my nuggets account and B station, if the content to help you, welcome everyone to like, collect + attention
link
-
Proficient in Vue stack source code principles
-
Form a complete set of video
-
Learning exchange group