Previously on & Background

The createElm method, which creates real elements from VNode, contains two scenarios:

  1. ifVNodeIs a custom componentcreateComponentMethod processing;
  2. If it is a normal element, it passesnodeOps.createElementCreate a nativeHTMLElements;

At the end of the last article, I combed the whole process of stack execution from new Vue to INSERT. This process is the whole process of Vue from new Vue to rendering to the page.

However, Vue is a responsive system, and the first half of the process is only completed, and the remaining half is when the responsive data changes, Vue’s rendering Watcher is notified to trigger the re-rendering, which is often referred to as the patch phase.

For the internal design of Vue itself, as long as the process of VNode rendering to the page is called patch, it is not divided into two big processes, although the internal differentiation is still the first rendering, you still respond to the change of data and render. But this is not friendly to beginners of Vue source code, so I smartly split it into two processes and gave them names;

  1. The first render we call himFirst apply colours to a drawingThat’s the front oneMount the stageFor the first timeThe templatebecomeDOMRender to page;
  2. This part right here is calledPatch phaseThe next stage is reactive data updates that trigger re-rendering, your favoritedom diffThis is the cutie stage (she beats you a thousand times and you treat her like your first love);

Ii. Process analysis + sample code

To accommodate responsive data updating during patch, we added a button button# BTN to the template. The handler of the button click event will modify the data.forprop.a property:

  • forPatchComputedThis computes the property dependencydata.forProp;
  • <some-com />component-receivedsomeKeySo is data bound to propdata.forProp

The test.html code is as follows:

<! DOCTYPEhtml>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Vue</title>
</head>
<body>
<div id="app">
 <button id="btn" @click="goForPatch">Make forProp. +</button>
 <some-com :some-key="forProp"><div slot="namedSlot">SLOT-CONTENT</div></some-com>
 <div>forPatchComputed = {{forPatchComputed}}</div>
 <div class="static-div">Static node</div>
</div>
<script src="./dist1/vue.js"></script>
<script>
  const sub = {
    template: ` 
       
slot-fallback-content {{ someKey.a + ' foo' }}
`
.props: { someKey: { type: Object.default () { return { a: '996-rejected!! '}}}};debugger new Vue({ el: '#app'.data: { forProp: { a: 100}},methods: { goForPatch () { this.forProp.a++ } }, computed: { // Calculate attributes forPatchComputed () { return this.forProp.a + ' reject 996'}},components: { someCom: sub } })
</script> </body> </html> Copy the code

So when data.forProp is modified, there are now three Watcher triggers to update:

  1. forPatchComputedEvaluate the corresponding of the propertywatcher, note the calculation of attributeswatcherlazy;
  2. some-comThe correspondingRender the watcher;
  3. div#appOf the template for this root instanceRender the watcher;

Trigger reactive updates

In the responsive system is an obvious observer mode, which requires us to make clear who is the observer, who is the observed, who is responsible for the communication between the two;

  • The observer isWatcherInstance,watcherFrom the literal it can be seen that the observer (watchEnglish translation is notWatch, watch~);
  • The observed is the data itself, for exampledata.forPropThis object;
  • The communication between the two isDepInstance,DepDependence is a noun, not a verbBe watcher rely onIf it becomes a verbRely on othersA); Every reactive data has itdep.depIn charge of collecting and using this datawatcher, and then the data changes to send a notification, letwatcherTake action;

3.1 Review the implementation of responsiveness

The responsivity of the data has three components:

  1. While initializing reactive datadatathroughdefineReactiveMethod (the core isObject.defineProperty) will bedata.forPropbecomegettersetterWhen accessedgetterCollection dependencies are triggered when they need to be changedsetterNotifications that depend on this datawatcherThe update;
  2. Initialization of reactive datacomputedThe logic of:
    • 2.1 Create one for each calculated propertywatcher, and createWatcherClassexpOrFnThat is, a function that requires a value, which is a method that is declared when evaluating attributes, as in the example above:forPatchComputed () { return this.forProp.a + 'reject 996!!!' };
    • 2.2 Calculated attributes arelazy watcherThat is, the evaluated property is only evaluated when it is accessed;
    • 2.3 When is it evaluated? By the time I’m interviewed,forPatchComputedIs the template corresponding to the root instanceRender functionWe’ll take it when we execute itforPatchComputedThe corresponding value, then evaluated;
  3. Modify thedata.forProp.aThe triggersetterAnd the frontgetterThree are knownwatcherDepending on thisforPropAt this point, notify them of the three updates;

Who is responsible for collecting dependent Watcher and notifying watcher of updates? Dep class, which creates an instance of Dep for each data during responsive initialization. Dep. denpend collection relies on Watcher, and dep.notify notifyWatcher of updates. The watcher update is implemented with the watcher.update() method;

function defineRective () {
  const dep = new Dep();
  
  Object.defineProperty(target, key {
    get () {
      if (Dep.target) dep.depend()
    }
    set () {
      dep.notify()
    }
  })
}
Copy the code

Click button# BTN to trigger the setter by modifying this.forprop.a to enter the setter:

3.2 Dep. Prototype. Notify

export default class Dep {

  // Zoom watcher into the dependency list for responsive data
  depend () {
    if (Dep.target) {
      // Dep.target is set in the getter for reactive data and the value is watcher instance
      Dep.target.addDep(this)
    }
  }

  notify () {
    // stabilize the subscriber list first
    const subs = this.subs.slice()

    // Go through the watcher stored in the DEP and execute watcher.update()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}
Copy the code

After clicking the button to debug the breakpoint, go to dep.notify and you will find three watcher in this.subs:

  1. The first one is forPatchComputed thisCalculate the property watcherAs shown in figure

2. The second isdiv#appCorresponding to the root instance templateRender the watcher:

  1. The third is<some-com />Custom componentRender the watcher:

3.3 Wather. Prototype. The update

export default class Watcher {
  constructor (.) {}
 
  update () {

    if (this.lazy) {
      // Lazy execution of watcher goes here
      // Set dirty to true,
      // When the getter for the calculated property is accessed
      // Triggers a recalculation of the result of the computed callback function
      this.dirty = true
    } else if (this.sync) {
      this.run()
    } else {
      // Update is usually in here,
      // Put watcher into the watcher queue,
      // Then update the queue asynchronously
      queueWatcher(this)}}}Copy the code

3.3.1 Calculate the update of attributes

3.3.2 Render watcher update

QueueWatcher is an important one, and we’ll give it a separate article;

Four,

As the first part of the patch phase, this essay mainly does the following work:

  1. To modify thetest.htmlAdded a function to modify reactive databutton#btnElement, and bind click event modificationdata.forProp.a;
  2. The whole responsive process is reorganized, including the process of collecting, modifying data and distributing updates. And make it clearWatcher,DepAnd the dependent and dependent relationships among responsive data as well as the collaboration process among them;
  3. By modifying thethis.forProp.aInto thedep.notify()“And then saw actionCalculate attributethelazy watcherOrdinary watcherwatcher.update()Different approaches in the method:
    • 3.1 Lazy watcher sets this.dirty to true; This invalidates the cache of computed attributes, and it reevaluates them when they are accessed again, a process that we discussed in detail in Watcher about computed caching;

    • 3.2 Ordinary watcher including rendering watcher and user Watcher will execute queueWatcher method for asynchronous queue update;