I want to illustrate the principle in very simple terms, the first time to understand it, you don’t have to look at the code.

Suppose I have a template:

 <div id="app">
     <span>{{msg}}</span>
     <div v-bind="msg"></div>
     <input v-model="msg" />
   </div>
Copy the code

Suppose I create a vue instance:

 const vm = new Vue({
     el: '#app'.data: {
       msg: 'Hello Vue'}})Copy the code

Pretend I have a picture:

The data was hijacked

First, Vue gets the data and uses Object.defineProperty to add get and set methods to the MSG in the data. Handling this process is called “observe”

Like this:

 class Observer {
   constructor (data) {
     this.walk(data)
   }
   walk (data) {
     // Determine whether data is an object
     if(! data ||typeofdata! = ='object') return
     // Iterate over all properties of the object
     Object.keys(data).forEach(key= > {
       this.defineReactive(data, key, data[key])
     })
   }
   defineReactive (obj, key, val) {
     // If val is an object, the interior is also converted to responsive data
     this.walk(val)
     let _this = this
     // Collect dependencies and send notifications
     let dep = new Dep()
     Object.defineProperty(obj, key, {
       enumerable: true.configurable: true,
       get () {
         // Collect dependencies
         Dep.target && dep.addSub(Dep.target)
         return val
       },
       set (newValue) {
         if (newValue === val) return
         val = newValue
         _this.walk(newValue)
         // Send notifications

     dep.notify()
   }
 })




}
}
Copy the code

}}

Template compilation

{{MSG}} – This parsing is called “Compiler” template parsing. After updating the value, Compiler tells watcher that I used the value data[MSG]. If it is set, you should give me the updated value and I should change the content.

Like this:

 /*** * functions: * Responsible for compiling the last class, parsing instructions/difference expressions * Responsible for the first rendering of the page * Re-rendering the view when the data changes * ****/


class Compiler {
constructor (vm) {
this.el = vm.$el
this.vm = vm
this.compile(this.el)
}
// Compile the last class to handle text nodes and element nodes
compile (el) {
let childNodes = el.childNodes



     Array.from(childNodes).forEach(node =&gt; {
         // Process text nodes
         if (this.isTextNode(node)) {
             this.compileText(node)
         } else if (this.isElementNode(node)) {
             // Process element nodes
             this.compileElement(node)
         }
         // Check whether node has child nodes
         if(node.childNodes &amp; &amp; node.childNodes.length) {this.compile(node)
         }
     })
 }
 // Compile element node, process instruction
 compileElement (node) {
     // Iterate over all attribute nodes
     Array.from(node.attributes).forEach(attr =&gt; {
         let attrName = attr.name
         if (this.isDirective(attrName)) {
             attrName = attrName.substr(2)
             let key = attr.value
             let eventType = &#39; & #39;
             if (attrName.startsWith(&#39; on:&#39;) ) { eventType = attrName.slice(3)
                 attrName = attrName.substr(0.2)}this.update(node, key, attrName, eventType)
         }
     })
     // Check if it is a command

 }
 update (node, key, attrName, eventType) {
     let updateFn = this[attrName + &#39; Updater&#39; ]  updateFn &amp; &amp; updateFn.call(this, node, this.vm[key], key, eventType)
 }
 // Process the V-text instruction
 textUpdater (node, value, key) {
     node.textContent = value
     new Watcher(this.vm, key, (newValue) =&gt; {
         node.textContent = newValue
     })
 }
 // Process the V-ON instruction
 onUpdater (node, value, key, eventType) {
     node.addEventListener(eventType, value.bind(this.vm))
     new Watcher(this.vm, key, newValue =&gt; {
         node.removeEventListener(eventType, value.bind(this.vm))
         node.addEventListener(eventType, newValue.bind(this.vm))
     })
 }
 // Handle v-HTML directives
 htmlUpdater (node, value, key) {
     node.innerHTML = value
     new Watcher(this.vm, key, (newValue) =&gt; {
         node.innerHTML = newValue
     })
 }
 modelUpdater (node, value, key) {
     node.value = value
     new Watcher(this.vm, key, (newValue) =&gt; {
         node.value = newValue
     })
     // Bidirectional binding
     node.addEventListener(&#39; input&#39; , () =&gt; {this.vm[key] = node.value
     })
 }
 // Compile the text node to handle the difference expression
 compileText (node){
     let reg = / \ {\ {(. +?) \} \} /
     let value = node.textContent
     if (reg.test(value)) {
         let key = RegExp.$1.trim()
         node.textContent = value.replace(reg, this.vm[key])


         // Create a Watcher object to update the view when the data changes
         new Watcher(this.vm, key, (newValue) =&gt; {
             node.textContent = newValue
         })
     }

 }
 // Determine if element attributes are directives
 isDirective (attrName) {
     return attrName.startsWith(&#39; v-&#39;)
 }
 // Determine if the element attribute is a text node
 isTextNode (node) {
     return node.nodeType === 3
 }
 // The node is an element node
 isElementNode (node) {
     return node.nodeType === 1}}Copy the code

}

Depend on the collection

Third, the watcher, after receiving the compiler’s action, can wait in data’s set method. But there may be many watcher, and one watcher will be created for every place where data[MSG] is used. You might as well call the Dep publisher and collect the Watcher. Watcher has an update method that executes a span callback whenever the observed value changes. Watcher saves the span callback and retrieves the current MSG value. If you get the MSG value, it will trigger the get method. Tell Dep to wait here and pack up when Watcher arrives.

Like this:

 class Watcher {
   constructor (vm, key, cb) {
     this.vm = vm
     this.key = key
     // The callback function that updates the view
     this.cb = cb

 // Record the watcher object to the Dep static attribute target
 Dep.target = this

 // Trigger the get method, in which addSub is called
 this.oldValue = vm[key]

 Dep.target = null




}
// Update the view when data changes
update () {
let newValue = this.vm[this.key]
if (this.oldValue === newValue) return
this.cb(newValue)
}
}
Copy the code

Update () {let newValue = this.vm[this.key] if (this.oldValue === newValue) return this.cb(newValue) } }

Distributed update

Fourth, Dep installs all the watchers into its sub (an array) in get. Once the MSG set is called, check to see if the MSG has changed. If the MSG has changed, remove the watcher from each sub and execute the update one by one. The update executes the innerHTML of the span, which is updated with the new value of MSG

 class Dep {
   constructor () {
     // Store all observers
     this.subs = []
   }
   // Add an observer
   addSub (sub) {
     if (sub && sub.update) {
       this.subs.push(sub)
     }
   }
   // Send notifications
   notify () {
     this.subs.forEach(sub= > {
       sub.update()
     })
   }
 }
Copy the code

Call from an entry file:

 class Vue {
     constructor(options) {
         // Save the data in the options via attributes
         this.$options = options  || {}
         this.$data = options.data || {}
         this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el

     // Convert the data members into getters and setters for the injected vue instance
     this._proxyData(this.$data)
     // Call an observer to listen for changes in data
     new Observer(this.$data)
     // Call the Compiler object to parse instructions and differential expressions
     new Compiler(this)
     
 }

 _proxyData (data) {
     // Iterate over all attributes in data
     Object.keys(data).forEach(key =&gt; {
         Object.defineProperty(this, key, {
             enumerable: true.configurable: true.get() {
                 return data[key]
             },
             set (newValue) {
                 if (newValue === data[key]) return
                 data[key] = newValue
             }
         })
     })
 }




}
Copy the code

}

The overall process is like this, if the simple model to figure out, and then look at the source ideas will be very clear.