My bronze version of vUE code address:GitHub | Yards cloud】

Cloud 】 【 making | yards – bronze version of vue code are TB vue source simplified annotation in detail can taste the rest assured

Implementation schematic diagram:

Vue.js initialization flowchart:Corresponding vUE source code

Data Responsive Observer principle:

Observer Role: PassedObject.definePropertytodataAll levels of data in the

class Observer {
  constructor(data) {
    //__ob__ A reactive flag that 'inherits' the current this from the object or array that needs the response
    Object.defineProperty(data, '__ob__', {
      value: this./ / points to this
      enumerable: false.// Not enumerable
      configurable: false
    })

    // Determine the array response
    if (Array.isArray(data)) {
      data.__proto__ = arrayMethods // Replace the encapsulated prototype method
      this.observeArray(DataCue)
    } else {
      this.walk(data)
    }
  }

  observeArray(data) {
    for (let i = 0; i < data.length; i++) {
      observe(data[i])
    }
  }

  walk(data) {
    Object.keys(data).forEach(key= > {
      this.defineReactive(data, key, data[key])
    })
  }

  defineReactive(data, key, value) {
    observe(value) // Recurse all data responses
    let dep = new Dep // One for each attribute
    Object.defineProperty(data, key, {
      get() {
        if (Dep.target) { // Assign dep. target and call get to add a wacher to the property
          dep.depend()    / / add a watcher
        }

        return value
      },
      set(newValue) {
        if (newValue === value) return
        observe(newValue) // Give the new data response
        value = newValue

        // View update
        dep.notify()
      }
    })
  }
}



export function observe(data) {
  // Not an object or =null not monitored
  if(! isObject(data)) {return
  }

  // The monitored object is displayed
  if (data.__ob__ instanceof Observer) {
    return
  }

  return new Observer(data)
}
Copy the code

Add get and set methods and extract the hijacked logic separately into defineReactive method. Observe method verifies data type. When the requirement is met, the Observer method is called for property responsiveness, and then the object is recycled for each property to be hijacked. When the data is an array, Observe the observe in the set method to deeply hijack the newly assigned object and ensure that the inserted data is converted into a response. In the defineReactive method, an instance of Dep is created for each attribute, a watcher is added to the attribute Dep when {{data}} reactive data appears on the page, and get is triggered when the render function executes, In get, you can add the watcher to the SUBs array of the Dep for uniform management. Because there are a lot of operations in the code to get the value of data, it will often trigger get, and we need to ensure that the Watcher will not be added repeatedly, so in the Watcher class, After getting the old value and saving it, dep. target is immediately assigned to null, and dep. target is short-circuited when GET is triggered, Dep and Watcher are many-to-many. The logic of one diff per component is one Watcher per component. That is, multiple responsive properties within a component page point to a Watcher and each attribute corresponds to a Dep, The DEP stores more than one Watcher, that is, the DEP appears in more than one Watcher, indicating that the property exists in multiple components page responsive display details please asynchronous source code

Principle of template compiler:

If you get the outerHTML of a node using document.querySelector(“div”).outerhtml you’ll see that it’s the tag code string that you wrote in your HTML file

  1. The first step we need to passparseHTMLMethods theouterHTMLConverted toastThe tree
  2. Step 2 we need to convert the AST tree torenderThe string form of the function
  3. And finally we go throughnew Function()The Render method converts the string form of the render function to the real onerenderMethod,renderThe function of a method is to generatevNodeFinally passeddiff Algorithm comparison of new and oldvNodeThus completed the page update rendering
export function compileToFunctions(template) {
  //1. Convert outerHTML to an AST tree
  let ast = parseHTML(template) // { tag: 'div', attrs, parent, type, children: [...] }
  // console.log("AST:", ast)

  //2. Ast tree => Concatenate strings
  let code = generate(ast) //return _c('div',{id:app,style:{color:red}}, ... children)
  code = `with(this){ \r\n return ${code} \r\n }`
  // console.log("code:", code)
  
  //3. String => Executable method
  let render = new Function(code)
  / * * is as follows:  * render(){ * with(this){ * return _c('div',{id:app,style:{color:red}},_c('span',undefined,_v("helloworld"+_s(msg)) )) *} *} * */

  return render
  OuterHTML => Ast tree * 2. ast tree => render string * 3. render string => render method */
}
Copy the code

Implementation principle of Patch.js Diff algorithm:

By comparing old and newvNodeUpdate the DOM render page differentlyWhat is a Vnode? Here is a prototype of a Vnode: el: is the actual node on the DOM corresponding to the current nodevNodeIf not, go straight throughelManipulate the actual DOM rendering page it corresponds toHTMLAs follows:

<div id="app">
  <h1 class="h1">Title 1 Name: {{name}}</h1>
  <h2 style="color: red;">Title 2 Age: {{age}}</h2>
  <div>
    <h3 class="h2">The title three</h3>
    <span style="color: pink; font-size: 30px;">Name: {{name}} age: {{age}}</span>
  </div>
</div>
Copy the code

First, there are three possible tree-level comparisons: add, delete and change.new VNodeDelete it if it doesn’t existold VNodeIf it does not exist, it increases Execute diff to perform update if both exist:

// Diff algorithm core vnode comparison to obtain the final DOM
SRC \core\vdom\patch.js 424 lines
oldStartIndex // Old start index
oldStartVnode // Old beginning
oldEndIndex   // Old tail index
oldEndVnode   // Get the last of the old children

newStartIndex // Old start index
newStartVnode // Old beginning
newEndIndex   // Old tail index
newEndVnode   // Get the last of the old children

while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
    //1 and 2 resolve the problem of setting nodes to null when arrays collapse

    //1. Check whether the old start node exists
    if(! oldStartVnode) { oldStartVnode = oldChildren[++oldStartIndex]//2. Check whether the old end node exists
    } else if(! oldEndVnode) { oldEndVnode = oldChildren[--oldEndIndex]//3. Whether the old and new start nodes are the same is the recursive patch comparison child node
    } else if (isSameVnode(oldStartVnode, newStartVnode)) {
      patch(oldStartVnode, newStartVnode)
      oldStartVnode = oldChildren[++oldStartIndex]
      newStartVnode = newChildren[++newStartIndex]
    
    //4. Whether the old and new end nodes are the same is the recursive patch comparison child node
    } else if (isSameVnode(oldEndVnode, newEndVnode)) {
      patch(oldEndVnode, newEndVnode);
      oldEndVnode = oldChildren[--oldEndIndex]; // Move the tail pointer
      newEndVnode = newChildren[--newEndIndex];

    //5. Whether the old start node and the new end node are the same is the recursive patch comparison child node
    } else if (isSameVnode(oldStartVnode, newEndVnode)) { // Reverst sort
      // Move the head and tail in reverse order
      patch(oldStartVnode, newEndVnode);
      parent.insertBefore(oldStartVnode.el, oldEndVnode.el.nextSibling); // Be mobile
      oldStartVnode = oldChildren[++oldStartIndex];
      newEndVnode = newChildren[--newEndIndex];

    //6. Whether the old end and new start nodes are the same is a recursive patch comparison child node
    } else if (isSameVnode(oldEndVnode, newStartVnode)) { // The old tail is compared with the new head
      patch(oldEndVnode, newStartVnode)
      parent.insertBefore(oldEndVnode.el, oldStartVnode.el)
      oldEndVnode = oldChildren[--oldEndIndex]
      newStartVnode = newChildren[++newStartIndex]
    }
    ...
Copy the code

Watcher asynchronous update queue principle:

Event Loop:The browser’s work mechanism for coordinating tasks such as event handling, script execution, network requests, and rendering.Macro Task Task:Stands for discrete, independent units of work. The browser completes one macro task and rerenders the page before the next macro task starts. It mainly includes creating document objects and parsingHTML, execution main lineJSCode and various events such asPage load,The input,Network eventsandThe timerAnd so on.

Microtasks: Microtasks are smaller tasks that are executed immediately after the execution of the current macro task. If there are any microtasks, the viewer will clear them and re-render them. Examples of microtasks are Promise callbacks, DOM changes, and so on. Experience the process of macro and micro tasks

Asynchronous: As long as it listens for data changes, Vue opens a queue and buffers all data changes that occur in the same event loop. Batch: If the same watcher is triggered more than once, it will only be pushed to the queue once. De-duplication is important to avoid unnecessary computation and DOM manipulation. Then, in the next event loop, “TICK,” Vue refreshes the queue to do the actual work. Asynchronous strategy: Vue internally attempts to use native Promise.then, MutationObserver, or setImmediate for asynchronous queues, and setTimeout instead if none of the execution environments support it.

let has = {}; // Vue source code sometimes to reuse is set sometimes with the object to achieve the de-duplication
let queue = [];

// Whether the queue is waiting for an update
function flushSchedulerQueue() {
  for (let i = 0; i < queue.length; i++) {
    queue[i].run() // Execute the updateComponent method inside Watcher to update the page
  }
  queue = []
  has = {}
}

// Since multiple elements refer to the same watcher, we need to cluster the watcher together to execute the update
// Reason: If you execute a Watcher for every element that matches, the performance of many of the same Watcher execution is significantly reduced
export function queueWatcher(watcher) {
  const id = watcher.id;

  if (has[id] == null) {
    has[id] = true // If the watcher has not been registered, register the watcher to the queue and mark it as registered
    queue.push(watcher)  // Watcher stores the updateComponent method to update the page
    console.log("queuequeue---", queue);
    nextTick(flushSchedulerQueue); // flushSchedulerQueue calls render watcher}}// 1. Callbacks [0] is the dep.notify() method of the flushSchedulerQueue function that listens for data changes in the component
// the 2.dep.notify () method passes all triggered watcher to queueWatcher
FlushCallbacksQueue works in flushCallbacksQueue when the queueWatcher method is used to flush the watcher
let callbacks = [];   // [flushSchedulerQueue,fn]
let pending = false
function flushCallbacksQueue() {
  callbacks.forEach(fn= > fn())
  pending = false
}


// The first time nextTick is entered, a timer is started to execute the nextTick callback function
// Since the JS timer is a macro task, the timer will wait until all the micro tasks have been executed before executing the timer
// So the flushCallbacksQueue method is triggered when the nextTick callbacks inside the component are added one by one and the page is completely rendered
export function nextTick(fn) {
  callbacks.push(fn)  / / image stabilization
  if(! pending) {// The concept of true event loops promise mutationObserver setTimeout setImmediate
    setTimeout(() = > {
      flushCallbacksQueue() // Clear callback queue
    }, 0)
    pending = true}}Copy the code