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