One: The first thing to know

  1. Data driven
  2. The core principles of responsiveness
  3. Publish subscribe mode and observer mode

Two: data driven

  1. Data responsiveness: The data model is just a normal javaScript object, and when we modify the data, the view is updated to avoid tedious DOM manipulation and improve development efficiency.
  2. Bidirectional binding: data changes, view changes; The view changes and the data changes with it. We can use v-Models to create two-way data binding on form elements.
  3. Data-driven is one of the most unique features of Vue, where you only need to focus on how the data is rendered into the view.

Three: data responsive core principle Vue2

  1. Object.defineProperty
  • A single attribute

    Let data = {// Vue data option MSG = “Hello Word”} // Vue instance let vm = {}

    Object.defineproperty (vm,” MSG “,{// Enumerable: // Deletes the specified information using delete and defineProperty. Get (){console.log(‘get:’,data.msg) return data.msg}, // Set (newValue){console.log(‘set:’newValue) if(newValue === data.msg){return} data.mag = newValue doucment.querySelector(“.app”).textContent = data.msg } })

    Vm. MSG = ‘ha ha ha’ console.log(vm. MSG)

  • Multiple attributes

    Let data = {Vue data MSG :’Hello Word’, count: 5}

    // let vm = {} proxyData(data) function proxyData(data){object.keys (data).foreach (key => { Object. DefineProperty (vm, key, {// enumerable: // Deletes the specified information using delete and defineProperty. true, get(){ console.log(‘get:’,key,data[key]) return data[key] }, Set (newValue){console.log(‘set:’,key,newValue) if(newValue === data[key]){return} data[key] = newValue // Data change, update Dom value document.querySelector(‘#app’).textContent = data[key] }, }) }) }

    vm.msg = ‘Hello World’ console.log(vm.msg)

Three: Data responsive core principle Vue3

  1. Proxy

  2. Listen directly on objects, not properties (so no traversal is required to process multiple properties)

  3. New in ES6, not supported by IE, new can be optimized by browser

    Let data = {MSG: ‘Hello Word’, count: 0}

    // Get (targer, key){console.log(“get”, key){// Get (targer, key){console.log(“get”, key); Target [key]) rerurn target[key]}, // Set (target, key, newValue){console.log(“set”, key, newValue) if(target[key] === newValue){ return } target[key] = newValue document.querySelector(‘#app’).textContent = target[key] }, })

    vm.msg = “Hello” console.log(vm.msg)

Four: publish and subscribe mode

  1. Publish subscribe model (subscriber, publisher, signal center)

  2. We assume that there is a ‘signal center’ where a task ‘publishes’ a signal to the signal center when it completes, and other tasks’ subscribe’ to the signal center to know when they can start executing. This is called the “publish subscriber model.”

  3. Vue User-defined event

    {click:[fn1,fn2],change:[fn1,fn2]}

    // The first parameter is an event, On (“dataChange”,()=>console.log(‘ dataChange ‘))vm.on(“dataChange”, () = > {the console. The log (‘ dataChange ‘)}) vm) on (” dataChange “, () = > console. The log (‘ dataChange ‘)) vm. On (” dataChange.” () => { console.log(“dataChange2”) })

    $emit(“dataChange”)

  • Sibling communication

    // let eventHub = new Vue()

    $emit(‘add-toda’,{text: this.newtodatext}) $emit(‘add-toda’,{text: this.newtodatext}) this.newTodaText = ” }

    $on(‘add-toda’, this.addToda)} $on(‘add-toda’,this.addtoda)} $on(‘add-toda’,this.addtoda)} $on(‘add-toda’,this.addtoda)}

Five: simulate publish and subscribe model

< script SRC = "https://cdn.jsdelivr.net/npm/vue" > < / script > / class/event trigger EventEmitter {/ / record all events and the corresponding handler constructor () { //{"click":[fn1,fn2]} this.subs = {}} The second parameter is the event function $on (eventType, hander) {this. Sub [eventType] = this. Subs/eventType | | [] this. Subs [eventType]. Push (hander)} $emit(eventType){if(this.subs[eventType]){this.subs[eventType].foreach ((hander) => {$emit(eventType){if(this.subs[eventType]){this.subs[eventType]. } / / test let en = new EventEmitter () / / registered event em. $on (" click ", () = > {the console. The log (" click1 ")}) em. $on (" click ", () = > { Console. log('click2')}) // Triggers the event em.$emit('click') browser outputs click1 click2Copy the code

Six: Observer mode

  1. Observer (Subscriber)-Watcher

  2. Update (): Exactly what to do when an event occurs

  3. Target (publisher) -dep

  4. Subs array: Stores all observers

  5. AddSub () : Adds an observer

  6. Notify: When an event is sent, the update() method of all observers is called

  7. No event center (unlike publishing subscribers)

  8. Simulated observer model

    Class Dep {constructor(){this.subs = []} // Add subscriber method addSub(sub){// Determine if the object exists and has update method if(sub &&) Sub.update){this.subs.push(sub)}} // Notify all subscribers of the occurrence of the event, Update (){this.subs.foreach ((sub) => {sub.update()})}}

    Update (){console.log(‘update’)}} class Watcher {// publisher – target // subscriber – observer

    let dep = new Dep() let watcher = new Watcher() dep.addSub(watcher) dep.notify()

Seven: the difference between publishing subscribers and observers

  1. The observer mode is scheduled by a specific target, such as an event, and the Dep calls the observer method, so there are dependencies between the observer mode subscribers
  2. The publish/subscribe model is invoked by the unified center, so publishers and subscribers do not need to know of each other’s existence

Eight: Simulation Vue response type principle – analysis

  1. Vue- Injects data members into Vue instances and converts data members into getters/setters
  2. Observer – Listens for all attributes of a data object, gets the latest values and informs Dep of any changes
  3. Compiler- Parses instructions/interpolations in each element and replaces them with the corresponding data
  4. The Dep(publisher) adds an observer (wathcer) to notify the observer when data changes
  5. Watcher – Updates the view with data changes

Eight: the Vue class

  1. Responsible for receiving initialization parameters (options)

  2. Is responsible for injecting properties from Data into vUE instances and converting them into getters/setters

  3. Responsible for calling the Observer to listen for changes to all properties in data

  4. Responsible for calling compiler to solve instructions/interpolation expressions

    class Vue { constructor(options){ //1. Through property preservation option data (the following attributes are used to record data from the options) / / if not by value is an empty object enclosing the options = options ∣ ∣ enclosing the options = options | | {} This. The options = options ∣ ∣ enclosing data = options. The data | | {} / / el Vue instance USES the root of the DOM element to this. El is the root of a Vue instance using the DOM elements This. el is the root DOM element used by Vue instances this.el = typeof options.el === “string”? document.querySelector(options.el) : options.el //2. Convert data members to getters and setters and inject this._proxyData(this.data)//3 into vue instance. NewObserver (this.data) //3 New Observer(this.data)//3 NewObserver (this.data) //4 New Compiler(this)} // Proxy data attribute _proxyData(data){// Traverses all attributes in data object.keys (data).foreach () (key) => {// Inject the data attribute into the Vue instance // in the arrow function, DefineProperty (this, key, {enumerable: true, signals: true, get(){ return data[key] }, set(newValue){ if(newValue === data[key]){ return } data[key] = newValue } }) }) } }

Class 9: the Observer

  1. Is responsible for converting properties in the Data option into responsive data

  2. An attribute in data is also an object, and the modified attribute is converted into responsive data

  3. Send notifications of data changes

  4. The defineReactive method in the Observer class passes the third attribute for: When accessing a value in the data property, The set() method in Object.defineProperty in VUE is first fired, and the set() in Object.defineProperty in Vue calls data[key](this.$data), and ob is fired when data[key] is called The get() method in server, which uses data[key] as above without the third argument, is looped

    Class Observer {constructor(data){// Call this. Walk (data)} // walk(data){//1. Check whether data is an object or null if(! data || typeof data ! Keys (data). ForEach ((key) => {this.definereactive (data, key); Data [key])})} / / call Object. Defineproperty convert properties to getter and setter definerReactive (obj, key, val) {let that = this / / is responsible for the collection, Let dep = new dep () // When val is an object (internal attributes are not converted to ser() and get()), so we need to call walk to convert this.walk(val). Object.defineProperty(obj, key, { enumerable: true, configurable: True, get(){dep.target && dep.addSub(dep.target) return val}, Set (newValue){if(newValue === val){return} val = newValue // Reassigning the data property to an object also requires the values in the new object as set() and get() this.walk(newValue). // Dep.notify ()},})}}

Ten: the Compiler class

  1. Responsible for compiling templates and parsing instruction/differential expressions

  2. Responsible for the first rendering of the page

  3. Re-render the view when the data changes

    Class Compiler {//vm is vue instance constructor(vm){this.el = vm.el this.vm = vm this.compile(this.el)} Compile (el){// Record node let childNodes = el.childNodes array. from(childNodes).foreach ((node) => {// Process text nodes This.iselementnode (node)){this.iselementNode (node)}else if(this.iselementNode (node)){this.iselelement (node)} Compile if(node.childNodes && node.childnodes.length){this.compile(node)}})} Fetch command compileElement(node){// fetch all attribute nodes array.from (node.attributes).foreach ((attr) => {// fetch let attrName = attr.name //text v-text v-model if(this.isDirective(attrName)){ //v-text ==> text attrName = attrName.substr(2) let key = attr.value this.update(node, key, attrName) } }) } update(node, key, AttrName){let updateFn = this.[attrName + ‘Updater’] TextUpdater (node, value, key){node.textContent = value new Watcher(this.vm, Key, (newValue) => {node.textContent = newValue})} key){ node.value = value new Watcher(this.vm, key, (newValue) => {node.value = value}) // Two-way binding Node.adDeventListener (‘input’,() => {this.vm[key] = node.value})} // Compile the text node, CompileText (node){{mag}} let reg = /\{\{(.+?)\}\}/ let value = node.textContent if(reg.test(value)){ 1. Trim () node.textContent = value.replace(reg, New watcher (this.vm, key, (newValue) => {node.textContent = newValue})}} isDirective(attrName){ If not, false return attrname.startswith (‘v-‘)} // Check whether the node is a text node isTextNode(node){//nodeType is 3 rerurn node.nodeType === IsElementNode (node){return node.nodeType === 1}}

Eleven: Dep class

  1. Responsible for collecting dependencies, adding Watcher

  2. Notify all observers

    Class Dep{costructor(){this.subs = []} // Add observer addSub(sub){if(sub && sub.update){ // Add to observer this.subs.push(sub)} // Notify (){this.subs.foreach ((sub) => {// call ypdate sub.update()})}}

Twelve: The Watcher class

Class Watcher{// The first is the Vue instance; the second is the value key; the third is the callback function constructor(vm, key, Cb){this.vm = vm // The data attribute name this.key = key // the callback is responsible for updating the view this.cb = cb // The watcher object is logged to the Dep static attribute target dep. target = this // Trigger the get method, This. OldValue = vm[key] // Set to null to prevent subsequent repetition of dep. target = null} // Update the view when the data changes Update (){let newValue = this.vm[this.key] if(this.oldValue === newValue){return} // Update this.cb(newValue)}}Copy the code