MVVM Principle-1-Principle Analysis

Posted by wyrd33 on Sat, 15 Feb 2020 18:34:15 +0100

MVVM - Introduction and Demo

What is MVVM implemented in Vue?

  • First, the first M refers to the Model, which is also the ** data model. In fact, it refers to the data, which is changed into the Vue. In fact, it refers to the data ** in the Vue component instance. But this data has been defined from the very beginning as responsive data.

  • The second V refers to the View, which is also the ** Page View, in Vue is the DOM object that our template converts to**

  • The third VM refers to the **ViewModel, the manager of views and data, which manages our data-to-view changes. In Vue, it refers to our current Vue instance, a bridge between Model data and View view communication**

  • In a nutshell: Data Driven View, Data Change=>View Update
<!-- view -->
<template>
  <div>{{ message }}</div>
</template>
<script>
// Model Common Data Object
export default {
  data () {
    return {
      message: 'Hello World'
    }
  }
}
</script>

<style>

</style>

MVVM-Responsive Principle-Object.defineProperty()-Basic Use

Next, we will focus on the principle and implementation of MVVM. The Vuejs website gives the principle of MVVM.

Vue Document Description

From the above documents, we can see that Vue's Response Principle (MVVM) is actually the following:

When you pass a normal JavaScript object into a Vue instance as the data option, Vue will iterate through all the properties of the object and use Object.defineProperty Convert all these attributes to getter/setter .Object.defineProperty is a feature of ES5 that does not shim, which is why Vue does not support IE8 and lower browsers.

From the above statement, we find several keywords, Object.defineProperty getter/setter

What is Object.defineProperty?

Definition: The Object.defineProperty() method defines a new property directly on an object, or modifies an existing property of an object and returns the object.

Syntax: Object.defineProperty(obj, prop, descriptor)

Parameter: obj =>Object on which to define properties.

Prop =>Property name to add or modify

Descriptor =>Attribute descriptor to be defined or modified.

Return value: The object passed to the function.That is, the obj object passed in

From the notes above, let's see what parameters we need to learn

obj is an object that can either be a new Object() or {}

prop is the property name, which is also a string

What is a descriptor and what are its properties

There are two main forms of attribute descriptors currently present in objects: data descriptors and access descriptors.A data descriptor is an attribute with a value that may or may not be writable.Access descriptors are properties described by getter-setter functions.Descriptors must be one of these two forms; they cannot be both.

The official description above tells us that there are ** two modes in the defineProterty design, one for data and one for access**

The descriptor must be one of the two, not both, that is, neither a mountain nor a tiger

Let's write an example of the simplest **data descriptor**

  var obj = {
            name: 'Xiao Ming'
        }
       var o = Object.defineProperty(obj, 'weight', {
            value: '280kg'
        })
        console.log(o)

Next, do a detailed analysis

Object.defineProperty() - Data Descriptor Mode

What are the properties of a data descriptor?

  • Value =>The value corresponding to this property.It can be any valid JavaScript value (number, object, function, etc.).Default to unfined
  • Writable =>value can only be changed by the assignment operator if and only if the property's writable is true.Default to false.

Just these two? Anything else?

  • Configurable =>The property descriptor can only be changed if and only if the property's configurable is true, and the property can also be deleted from the corresponding object.Default to false.
  • Enumerable =>This property can only appear in the enumeration property of an object if and only if the enumerable of the property is true.Default to false.

Why do configurable and enumerable not write with value and writable at the same time?

Because these two attributes can appear not only in the data descriptor but also in the access descriptor

We write a writeable property and an unwritable property by using the writeable and value properties

   var obj = {
          name: 'Xiao Ming'
      }
     Object.defineProperty(obj, 'money', {
         value: "10k" // Salary is not changeable at this time
     })
     Object.defineProperty(obj, 'Xiao Ming', {
         value: '150 Jin', // Give 10,000 hairs
         writable: true
     })
     obj.money = '20k'
     obj.weight = '200 Jin'
     console.log(obj)

Next, we want to make an immutable property mutable

    var obj = {
          name: 'Xiao Ming'
      }
     Object.defineProperty(obj, 'money', {
         value: '10k', // Salary is not changeable at this time
         configurable: true  // The writeable property can only be changed if this is true
     })
     Object.defineProperty(obj, 'weight', {
         value: '150 Jin', // Give 10,000 hairs
         writable: true
     })
     obj.money = "20k"
     obj.weight = '200 Jin'
     console.log(obj)
     Object.defineProperty(obj, 'money', {
         writable: true
     })
     obj.money = '20k'
     console.log(obj)

Next, we want to be able to iterate through the two newly added attributes while traversing

      var obj = {
          name: 'Xiao Ming'
      }
     Object.defineProperty(obj, 'money', {
         value: '10k', // Salary is not changeable at this time
         configurable: true,
         enumerable: true
     })
     Object.defineProperty(obj, 'weight', {
         value: '150 Jin', // Give 10,000 hairs
         writable: true,
         enumerable: true

     })
     obj.money = "20k"
     obj.weight = '200 Jin'
     console.log(obj)
     Object.defineProperty(obj, 'money', {
         writable: true
     })
     obj.money = '20k'
     console.log(obj)
     for(var item in obj) {
         console.log(item)
     }

Object.defineProperty() - Access Descriptor Mode

In the previous section, the unique attributes of data descriptors are value and writable, which also means that in access description mode

value and writable attributes cannot appear

So what are the properties of the storage descriptor?

  • Gett is a method that provides getters to properties, or undefined if there are no getters.When the property is accessed, the method is executed, and no parameters are passed in when the method is executed, but a this object is passed in (where this is not necessarily the object defining the property due to inheritance).
  • set is a method that provides a setter to an attribute, or undefined if no setter exists.This method is triggered when the attribute value is modified.The method will accept the unique parameter, which is the new parameter value for the attribute.

get/set is actually our most common read value and set value method This.name read value this.name ='Zhang San'

Call get method when reading value

Call set method when setting value

We do a way to read settings through get and set

  var obj = {
          name: 'Xiao Ming'
      }
      var wife = 'Little Joe'
      Object.defineProperty(obj, 'wife',{
          get () {
              return wife
          },
          set (value) {
             wife = value
          }
      })
      console.log(obj.wife)
     obj.wife= 'Jo'
      console.log(obj.wife)

But what do we want to do with traversal? Note that, when you store descriptors, you still have configurable and enumerable properties.

Still configurable

   // Define an object
        var person = {
            name: 'Cao Yang'
        }
        var name = 'Little Joe'
        Object.defineProperty(person, 'wife', {
            enumerable: true,  //Indicates that new attributes can be traversed
            // Access Descriptor
            get (){
                return  name  // Returns the properties of wife
            },
            set (value){
                name = value
            }
        })
        console.log(person.wife)
        person.wife = 'Jo' // value is not required to write to access descriptors
        console.log(person.wife)
        for(var item in person) {
            console.log(item)
        }

Data descriptor wriable only controls the value when the data is described and cannot be written with access descriptors

Object.defineProperty() - Simulate vm object

You have learned the basic use of defineProperty in two subsections. Next, we will simulate the effect of Vue instantiation through defineProperty

When Vue is instantiated, we explicitly assign data, but it can be accessed and set through **vm instance.property**

How?

var vm = new Vue({
  data: {
      name: 'Zhang San'
  }
})
vm.name = 'Li Si'

This is actually achieved through Object.defineProperty

 var person = {
       name: 'Xiao Fang'
    }
    var vm = {}  // vm object
    //    Object.defineProperty access descriptor
    Object.defineProperty(vm, 'name', {
        // Access descriptor get/set
        get () {
            return  person.name //Returns the value of the same property as person
        },
        set (value) {
            debugger
        //    It's time to give the values in person, too
          person.name = value
        }
    })
    console.log(vm.name) // Get value=> get method
    vm.name = 'Curly Bubble Face' // set method called
     console.log(vm.name)

In the code above, we implemented a data proxy in vm to change name directly from person to vm or person

Summary: We proxied the data in person in the access descriptors of set and get.

MVVM =>Data Agent=> Object.defineProperty =>Access Descriptor get/set =>Agent Data

MVVM not only takes this data, but also updates it to the DOM responsively, that is, when the data changes, we want to reflect the data** to the view

By debugging, we found that we can listen for data changes in the set function, just notify the corresponding view to update when the data changes.

So how do I tell you? What technology do I use? We'll bring up a publish-subscribe model in the next section

Introduction to publish subscription mode

What is the publish subscription model?

Actually, we've used it many times already. Publish/Subscribe means someone ** post a message **, someone subscribes to a message, and at the data level, more=>more

That is, program A can trigger or subscribe to multiple messages

One **eventBus** used in the vue project is a reflection of the publish subscription model

What do we bring this pattern for?

In the last section, we were able to capture changes in our data, and now we're going to try to change our view by publishing subscriptions as the data changes

Let's start with a few elements of the core code for this publishing subscription

First, we want publish subscription objects to be instantiated

Publish message $emit

Subscription Message$on

Based on these ideas, we get the following code

      //  Create a constructor
      function Events () {}
    //    Subscription Message Listening Message
      Events.prototype.$on = function() {}
    //   Publish a message
      Events.prototype.$emit = function (){}

Implementation of Publish Subscription Mode

  <button onclick="emitEvent()">Trigger Event</button>
    <script>
        //  Create a constructor
        function Events () {
            // Constructor
            // Opening up a space is only valid for the current instance
            this.subs = {} // The event name and callback function {key (event name): [callback function 1, callback function 2...] value (callback function)}
        }
    //    Subscription message listens for message eventName event name, fn is the callback function that should be triggered when the event is triggered
      Events.prototype.$on = function(eventName,fn) {
        //   Event name=>Callback function=>Find the callback function corresponding to this event and execute it when an event is triggered
        //  if(this.subs[eventName]) {
        //      this.subs[eventName].push(fn)
        //  }else {
        //      this.subs[eventName] = [fn]
        //  }
        this.subs[eventName] = this.subs[eventName] || []
        this.subs[eventName].push(fn)
      }
    //   The first parameter to publish a message must be eventName (the name of the event to trigger)... params represents all parameters after eventName
      Events.prototype.$emit = function (eventName, ...params ) {
        //  Get the name of the event should go to our open space to find if there is a callback function
          if(this.subs[eventName]) {
            //   Someone is listening to you
            // Callback function of others
            this.subs[eventName].forEach(fn => {
                // Change this orientation
               //  fn(...params) //Call the callback function and pass parameters
                 // Three ways to change the this point in the callback function
               //   fn.apply(this, [...params]) // apply parameter [parameter list]
              //  Fn.call(this,... Params)//several parameters
               fn.bind(this, ...params)() // Bind usage bind does not execute a function but changes the function this directly
            });
          }
      }

       var event = new Events()  // instantiation
      //   Open a listen
       event.$on("changeName", function(a,b,c, d, e) {
           console.log(this)
           alert(a + '-' +b +'-'+ c + '-'+ d +'-'+ e)
       }) // Listen for an event

    //    Call Trigger Method
       var emitEvent = function () {
         event.$emit("changeName", 1,2,3,4,5)
       }
    </script>

This uses the call/apply/bind method to modify the this point inside the function

Publish-subscribe mode allows you to notify a lot of people to do things when an event triggers, and what Vue does is update the DOM

MVVM Implementation-DOM Review

We learned Object.defineProperty and Publish-Subscribe modes, and almost had the ability to write an MVVM by hand.

But before we implement MVVM, let's review the meaning and structure of View, Dom

What is DOM?

Document object model document

What does Dom do?

Page elements can be manipulated through ** objects **

What types of object nodes are in the Dom

You can check with a small example below

  <div id="app">
        <h1>Our wills unite like a fortress,Co-Resistance to Epidemics</h1>
        <div>
            <span style='color:red;font-weight: bold;'>Pedestrian A:</span>
            <span>Wishing all heroes a safe return</span>
        </div>
    </div>
    <script>
       var app = document.getElementById("app")
       console.dir(app)
    </script>

Looking at the output above, we can see that

Node type of element type nodeType is 1 text type 3, Everything inside the document object is **Node**

childNodes is all nodes children refers to all elements => nodeType =1 nodes

All child nodes are placed under the property childNodes, which is a pseudo array=>pseudo array and does not have an array method. Has the len gt h property

What is the set of attributes for all tags?

Attributes =>Places all attributes

What does analysing DOM objects do? The data capture and publishing subscriptions we prepared earlier are meant to update the DOM

Let's start handwriting an MVVM example

Handwrite a simple version of vuejs Object.defineProperty =>Add Attributes.Modify Attribute Data Agent

Publish Subscription=>Publish Event Subscription Events

Dom =>Update View

MVVM Implementation-Implementing Vue's Constructor and Data Proxy

The challenge is to write a simple ** vuejs ** handwriting to enhance our own technical capabilities.

We want to implement the constructor for mvvm

Constructor mimics vuejs with data/el

The data is ultimately proxied to the current vm instance, either through the vm or through this.$data

        // Handwriting a simple version of vuejs for mvvm
        // options is the option that all vue attributes have $
        function Vue (options) {
            this.$options = options  // Placement options
            this.$el =
           typeof options.el === 'string' ? document.querySelector(options.el) : options.el
           // Assigning a dom object to $el is consistent with the official vuejs
         this.$data = options.data || {}
         //  Data proxy wants vm to be able to proxy data for $data
         // Hope vm.name is $data.name
         this.$proxyData() // Proxy data
        }
        // A good way to proxy data
        Vue.prototype.$proxyData = function () {
            // this is the current instance
            // Key is every key in the data
            Object.keys(this.$data).forEach(key => {
                Object.defineProperty(this, key, {
                    // Access Descriptor
                    get () {
                       return this.$data[key]  // Return data from $data
                    },
                    // Setting up the data requires setting the value to the value of $data and determining whether the data is equal before setting it up
                    set (value) {
                        // Value is a new value If the new value equals the old value, there is no need to set it again
                        if (this.$data[key] === value ) return
                        this.$data[key] = value // If you don't wait to set the value again
                    }
                })
            })

        }
     var vm =  new Vue({
            el: '#app', //and possibly other selectors and possibly dom objects
            data: {
                name: 'Lvpu',
                wife: 'army officer's hat ornaments'
            }
        })
        vm.wife = 'Xishi'
        vm.name = 'Xiao Ming'
        console.log(vm.name)

MVVM Implementation-Data Hijacking Observer

OK, this next step is critical. We want to do ** data hijacking **, who is hijacked and why?

In the code in the last section, we can either use vm.name='value'or vm.$data.name ='value', so where can we capture changes in the data?

Either this.data or this.$data changes the data for $data, so we need to ** hijack ** the data for $data, that is, listen to its set

Data Hijacking means that we want to monitor changes in the data layer of Model s in MVVM

    // Data hijacking
        Vue.prototype.$observer = function () {
            // Who is to be hijacked? $data
            // Traverse all key s in $data
            Object.keys(this.$data).forEach(key => {
               // Hijacking=>Changes in Hijacking data->Listening for Changes in data=> Set Method
               // obj / prop / desciptor
               let value = this.$data[key] // Re-open up a space value
               Object.defineProperty(this.$data, key, {
                   // How many descriptors are there? Data descriptor (value,writable) access descriptor (get/set)
                   get () {
                       return value
                   },
                   set (newValue) {
                      if(newValue === value) return
                      value = newValue
                    //   Once you enter the set method, the M in MVVM changes and the data changes
                    // MVVVM => Model =>Publish Subscription Mode=>Update Dom View
                   }
               })
            })
        }

Complete hijacking of data in constructor

  function Vue (options) {
            this.$options = options  // Placement options
        this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el
           // Assigning a dom object to $el is consistent with the official vuejs
         this.$data = options.data || {}
         //  Data proxy wants vm to be able to proxy data for $data
         // Hope vm.name is $data.name
         this.$proxyData() // Proxy data proxy data from $data to vm instance
         this.$observer()  // data Hijacking Hijacking $data Changes
        }

MVVM Implementation-Compiler Compiler-Design Architecture

Proxy: All data in $data is proxied to this

Hijacking: $data changes

Now that we've basically instantiated the data and finished proxying and hijacking it, we need to implement a few methods

When the data changes=>Convert the template to the latest object based on the latest data

Determine whether a node is a text node

Determine whether a node is an element node

Determine if directive v-model / v-text

Processing Element Nodes

Processing text nodes

So let's define the following methods

       // Execution of a general method constructor for compiling templates
       Vue.prototype.$compile = function () {

       }
       // Processing text node nodeType =3
       Vue.prototype.$compileTextNode = function () {}

       // Element node when processing element node nodeType = 1
       Vue.prototype.$compileElementNode = function () {}

      //  Determine whether a node is a text node
       Vue.prototype.$isTextNode = function () {}
      // Determine whether a node is an element node

       Vue.prototype.$isElementNode = function () {}

      // Determines whether an attribute is an instruction that all instructions start with v-
      Vue.prototype.$isDirective = function () {}

MVVM implementation-compile template Compiler implements basic framework logic

Now that we've got $el, the dom element of the page, through the constructor, we can implement the basic logic of compilation

Note: Text nodes no longer have child nodes because text is the final representation

Element node must have children

        // Compile Template
 // Execution of a general method constructor for compiling templates
        // Rootnode is the root node that passes into this loop=>Find all the child nodes under rootnode=>Child node=>Child node=>Child node=>Child node>Child node... until no child node is found
       Vue.prototype.$compile = function (rootnode) {
         let nodes = Array.from(rootnode.childNodes)  // Is a pseudo array converting a pseudo array to a true array
         nodes.forEach(node => {
            //  Loop through each node to determine the node type If you are a text node, use the text node's handling. If an element node requires the element node's handling
            if(this.$isTextNode(node)) {
                // If it is a text node
                this.$compileTextNode(node) // Processing text node Current node No more child nodes Necessary to continue finding
            }
            if(this.$isElementNode(node)) {
                // If it is an element node
                this.$compileElementNode(node) // Processing Element Nodes
                // If there must be children beneath the element node, only the text node is the end point
                // Recursive=>calls itself
                this.$compile(node) // Pass-through parameter guarantees automatic stop when finding node.chidNodes with length 0
                // It guarantees that all nodes under $el will be traversed once
            }
         })
       }
       // Processing text node nodeType =3
       Vue.prototype.$compileTextNode = function () {}

       // Element node when processing element node nodeType = 1
       Vue.prototype.$compileElementNode = function () {}

      //  Determines whether a node is a text node nodeType ===3
       Vue.prototype.$isTextNode = function (node) {
          return node.nodeType === 3  // Representation is a text node
       }
      // Determine whether a node is an element node

       Vue.prototype.$isElementNode = function (node) {
        return node.nodeType === 1  // Representation is element node
       }

The basic logic of the above code is that when a text node is encountered, it is treated as a text node. When an element node is encountered, it is treated as an element node.

If an element node is encountered, it means that ** is not finished ** Need to call the next level of lookup

MVVM Implementation-Compiler Compiler-Processing Text Node

  // Processing text node nodeType =3
       Vue.prototype.$compileTextNode = function (node) {
            // console.log(node.textContent)
            // What to do after getting the contents of the text node {{name}} =>Real value
            // regular expression
            const text = node.textContent // Get the contents of the text node to see if there are any interpolation expressions
             const reg = /\{\{(.+?)\}\}/g  // Will match all {{unknown}}
            if (reg.test(text)) {
                // If a match can be made to indicate that there is an interpolation expression in this text
                 // The value representing the last matching regular expression
                const key = RegExp.$1.trim() // Name attribute=>value of name $1 takes the first key
                 node.textContent = text.replace(reg,  this[key] )
                  // Gets the value of the property and replaces the interpolation expression in the text node
            }
       }

Tip: You don't need to remember but you need to understand when developing

MVVM Implementation-Compiler Compiler-Processing Element Node


       // Element node when processing element node nodeType = 1
       Vue.prototype.$compileElementNode = function (node) {
           // Instruction v-text V-model =>Data change=>View update update update data change
           // v-text ='value'=> textContent on innerText
           // Get all the properties of the node
          let attrs = Array.from(node.attributes) // Convert all attributes to an array
        // Loop whether each attribute attribute attribute has V-IF v-denotes instructions
            attrs.forEach(attr => {
               if (this.$isDirective( attr.name)) {
                //   Judging Instruction Type
                    if(attr.name === 'v-text') {
                        // The v-text directive means that the value expressed after the v-text acts on the innerText or textContent of the element
                      node.textContent = this[attr.value]   // assignment
                    }
                    if(attr.name === 'v-model') {
                        // Indicates that I want to bind the current node in both directions
                      node.value =  this[attr.value]   // v-model assigning value is not textContent
                    }

               } // If you start with v-it's an instruction
            })
       }

MVVM Implementation-Data Driven View-Publication Subscription Manager

Now that we have responsive data and compilation templates, we need to compile them as the data changes

As mentioned earlier, this step requires publishing subscriptions, so we implement publishing subscriptions on a Vue-based basis

        // Handwriting a simple version of vuejs for mvvm
        // options is the option that all vue attributes have $
        function Vue (options) {
            this.subs = {} // Event Manager
            this.$options = options  // Placement options
           this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el
           // Assigning a dom object to $el is consistent with the official vuejs
         this.$data = options.data || {}
         //  Data proxy wants vm to be able to proxy data for $data
         // Hope vm.name is $data.name
         this.$proxyData() // Proxy data proxy data from $data to vm instance
         this.$observer()  // data Hijacking Hijacking $data Changes
         this.$compile(this.$el) // Template first compilation rendering recursion requirement parameter must be passed in here
         // Recursion is a simple algorithm=>commonly used for processing tree data, nested data China/Beijing/Haidian/Zhongguancun/Zhichunlu/Haidianqiao/982/person
         // Recursion is essentially the function itself calling itself=>The condition passed in for the next recursion=>The same condition for two recursions=>Dead Loop
        }
   // Publish Subscription Manager for Vue$on $emit
      //  Listen for events
      Vue.prototype.$on = function (eventName, fn) {
                  //   Event name=>Callback function=>Find the callback function corresponding to this event and execute it when an event is triggered
        //  if(this.subs[eventName]) {
        //      this.subs[eventName].push(fn)
        //  }else {
        //      this.subs[eventName] = [fn]
        //  }
        this.subs[eventName] = this.subs[eventName] || []
        this.subs[eventName].push(fn)
      }
      // Trigger Event
  Vue.prototype.$emit = function (eventName, ...params) {
       //  Get the name of the event should go to our open space to find if there is a callback function
       if(this.subs[eventName]) {
            //   Someone is listening to you
            // Callback function of others
            this.subs[eventName].forEach(fn => {
                // Change this orientation
               //  fn(...params) //Call the callback function and pass parameters
                 // Three ways to change the this point in the callback function
               //   fn.apply(this, [...params]) // apply parameter [parameter list]
              //  Fn.call(this,... Params)//several parameters
               fn.bind(this, ...params)() // Bind usage bind does not execute a function but changes the function this directly
            });
          }
      }

MVVM implementation - driving view changes as data changes

Everything is ready now, only the east wind

Our data proxy, data hijacking, template compilation, event publishing subscriptions all work now by publishing through events when data changes, and then

Notification data can be compiled

        // Data hijacking
        Vue.prototype.$observer = function () {
            // Who is to be hijacked? $data
            // Traverse all key s in $data
            Object.keys(this.$data).forEach(key => {
               // Hijacking=>Changes in Hijacking data->Listening for Changes in data=> Set Method
               // obj / prop / desciptor
               let value = this.$data[key] // Re-open up a space value
               Object.defineProperty(this.$data, key, {
                   // How many descriptors are there? Data descriptor (value,writable) access descriptor (get/set)
                   get () {
                       return value
                   },
                   set: (newValue) => {
                      if(newValue === value) return
                      value = newValue
                    //   Once you enter the set method, the M in MVVM changes and the data changes
                    // MVVVM => Model =>Publish Subscription Mode=>Update Dom View
                      // Global compilation is performed only once to trigger an event view layer to listen for an event by publishing subscription mode
                     // Trigger an event
                     this.$emit(key) // Trigger an event with an attribute as the event name
                   }
               })
            })
        }

Listen for data changes

    // Compile Template Data Changed=>Update Template Data to Latest

       // Execution of a general method constructor for compiling templates
        // Rootnode is the root node that passes into this loop=>Find all the child nodes under rootnode=>Child node=>Child node=>Child node=>Child node>Child node... until no child node is found
       Vue.prototype.$compile = function (rootnode) {
         let nodes = Array.from(rootnode.childNodes)  // Is a pseudo array converting a pseudo array to a true array
         nodes.forEach(node => {
            //  Loop through each node to determine the node type If you are a text node, use the text node's handling. If an element node requires the element node's handling
            if(this.$isTextNode(node)) {
                // If it is a text node
                this.$compileTextNode(node) // Processing text node Current node No more child nodes Necessary to continue finding
            }
            if(this.$isElementNode(node)) {
                // If it is an element node
                this.$compileElementNode(node) // Processing Element Nodes
                // If there must be children beneath the element node, only the text node is the end point
                // Recursive=>calls itself
                this.$compile(node) // Pass-through parameter guarantees automatic stop when finding node.chidNodes with length 0
                // It guarantees that all nodes under $el will be traversed once
            }
         })
       }
       // Processing text node nodeType =3
       Vue.prototype.$compileTextNode = function (node) {
            // console.log(node.textContent)
            // What to do after getting the contents of the text node {{name}} =>Real value
            // regular expression
            const text = node.textContent // Get the contents of the text node to see if there are any interpolation expressions
             const reg = /\{\{(.+?)\}\}/g  // Will match all {{unknown}}
            if (reg.test(text)) {
                // If a match can be made to indicate that there is an interpolation expression in this text
                 // The value representing the last matching regular expression
                const key = RegExp.$1.trim() // Name attribute=>value of name $1 takes the first key
                 node.textContent = text.replace(reg,  this[key] )
                  // Gets the value of the property and replaces the interpolation expression in the text node
                this.$on(key, () => {
                    // Update the view in the callback function if the value represented by the key property changes
                    node.textContent = text.replace(reg, this[key] )    // Replace the original bracketed content with the latest value and assign it to textContent
                })
            }
       }

       // Element node when processing element node nodeType = 1
       Vue.prototype.$compileElementNode = function (node) {
           // Instruction v-text V-model =>Data change=>View update update update data change
           // v-text ='value'=> textContent on innerText
           // Get all the properties of the node
          let attrs = Array.from(node.attributes) // Convert all attributes to an array
        // Loop whether each attribute attribute attribute has V-IF v-denotes instructions
            attrs.forEach(attr => {
               if (this.$isDirective( attr.name)) {
                //   Judging Instruction Type
                    if(attr.name === 'v-text') {
                        // The v-text directive means that the value expressed after the v-text acts on the innerText or textContent of the element
                      node.textContent = this[attr.value]   // Assignment attr.value => v-text="name"
                      this.$on(attr.value, () => {
                        node.textContent = this[attr.value]   //The data is now updated
                      })
                    }
                    if(attr.name === 'v-model') {
                        // Indicates that I want to bind the current node in both directions
                      node.value =  this[attr.value]   // v-model assigning value is not textContent
                      this.$on(attr.value, () => {
                        node.value = this[attr.value]   //The data is now updated
                      })
                    }

               } // If you start with v-it's an instruction
            })
       }

Then let's write an example to test one

MVVM Implementation-View Change Update Data

Finally, we want to achieve a two-way binding where the data changes at the same time as the view changes

       // Element node when processing element node nodeType = 1
       Vue.prototype.$compileElementNode = function (node) {
           // Instruction v-text V-model =>Data change=>View update update update data change
           // v-text ='value'=> textContent on innerText
           // Get all the properties of the node
          let attrs = Array.from(node.attributes) // Convert all attributes to an array
        // Loop whether each attribute attribute attribute has V-IF v-denotes instructions
            attrs.forEach(attr => {
               if (this.$isDirective( attr.name)) {
                //   Judging Instruction Type
                    if(attr.name === 'v-text') {
                        // The v-text directive means that the value expressed after the v-text acts on the innerText or textContent of the element
                      node.textContent = this[attr.value]   // Assignment attr.value => v-text="name"
                      this.$on(attr.value, () => {
                        node.textContent = this[attr.value]   //The data is now updated
                      })
                    }
                    if(attr.name === 'v-model') {
                        // Indicates that I want to bind the current node in both directions
                      node.value =  this[attr.value]   // v-model assigning value is not textContent
                      this.$on(attr.value, () => {
                        node.value = this[attr.value]   //The data is now updated
                      })
                      node.oninput = () => {
                        //   Need to assign the value of the current most recent node to its own data
                        this[attr.value] =  node.value  // View Occurs=>Data Changes
                      }  // If an element is bound to the v-model directive, it should listen for value change events for that element
                    }

               } // If you start with v-it's an instruction
            })
       }

Topics: Front-end Vue Attribute Javascript