At the heart of Vue.js is a set of “responsive systems”.

“Responsive” means that when data changes, Vue notifies the code that uses the data. For example, if data is used in a view rendering, the view is automatically updated when the data changes.

As for the description of responsive data on the official website, it is not clear how it works in a short time. I’ll take a look at the Vue2.0 responsive core code implementation as I understand it.

In Vue, responsive data is divided into the following types: Object types{}And array types[]

Object type {}

To implement reactive, we need the following classes and methods:

  • datadata
  • Data listenerdefineReactive
  • Subscriber (update view)watcher
  • Maintain subscribersdep
  • stateactive

How it works: Attributes are hijacked internally by the defineReactive method using Object.defineProperty (only existing attributes are hijacked).

Suppose there is a container APP on the page, and data stores responsive variables. When the value in data changes, the value in the container also changes.

    <div id="app"></div>
    
    <script>
        let data = {
            count:0
        }
        app.innerHTML = data.count
    </script>
Copy the code

1. Add view update watcher

    <div id="app"></div>
    <script>
        let data = {
            count:0
        }
        // Define the watcher function, pass it as a function, and execute it immediately
        let watcher = (fn) = >{
            fn();
        }

        watcher(() = >{
            // Change the app content
            app.innerHTML = data.count;
        })
    </script>
Copy the code

What Watcher does is update the content on the page => update the view

2. Implement data listeners

Add the get() and set() methods to the data attributes, so that when the user values, they are collected as templates. Waiting for data change notification template data update.

    <script>
        let data = {
            count:0
        }
        // Data listener
        function defineReactive(obj) {
            // Each attribute redefines get and set
            for(let key in obj){
            	let value = obj[key]
                Object.defineProperty(obj,key,{
                    // Get () when a value "appears" in data
                    get(){
                    	// Return the original value
                        return value;
                    },
                     // When the value in data "changes", execute get ()
                    set(newValue){
                        value = newValue
                    }
                })
            }
        }
        // Hijack the data in data
        defineReactive(data)

        // At this time, a is not detected by the data listener, belonging to the "later" and not hijacked
        data.a = 10;

        // Define the watcher function, pass it as a function, and execute it immediately
        let watcher = (fn) = >{
            fn();
        }

        watcher(() = >{
            / / value
            app.innerHTML = data.count;
        })
    </script>
Copy the code

3. Update the view as data changes

   <div id="app"></div>
   <script>
       let data = {
           count:0
       }
       // View content to execute
       let active;
       // Data listener
       function defineReactive(obj) {
           for(let key in obj){
               let value = obj[key];
               // Store all methods associated with the current attribute
               let dep = [];
               Object.defineProperty(obj,key,{
                   // Get () when a value "appears" in data
                   get(){
                       / / (3)
                       if(active){
                           dep.push(active)
                       }
                       return value
                   },
                    // When the value in data "changes", execute get ()
                   set(newValue){
                       value = newValue
                       dep.forEach(active= >active())
                   }
               })
           }
       }
       // Hijack the data in data
       defineReactive(data)


       // Define the watcher function, pass it as a function, and execute it immediately
       let watcher = (fn) = >{
           active = fn;
           / / (1)
           fn();
           / / (4)
           active = null;
       }

       watcher(() = >{
           // Change the app content
           / / (2)
           app.innerHTML = data.count;
       })
   </script>
Copy the code

When defining watcher, execute (1)=>(2)=>(3)=>(4) in sequence. Each attribute has its own DEP attribute, which stores the watcher it stores. When the attribute changes, the corresponding Watcher will update it.

Vue2.0 is reactive with Object. DefineProperty Vue3.0 is reactive with proxy

If object.defineProperty is used when multiple layers of nested data exist in data, internal recursion will occur and performance will be affected. Proxy improves performance, but is incompatible with IE11.

Array type []

Instead of intercepting each entry of an array for performance reasons, we chose to override methods on the array stereotype (push, POP, Shift, unshift, splice, sort, reverse). Only these 7 methods override arrays

    <div id="app"></div>
    <script>
        let data = [1.2.3];
        // Get all the methods in the array - prototype chain
        let originArray = Array.prototype;
        / / shallow copy
        let arrayMethods = Object.create(originArray);
        // Data listener
        function defineReactive(obj) {
            // function hijacking, override method. You can add what you want to do
            arrayMethods.push = function (. args) {
                // Change the this pointer
                originArray.push.call(this. args); render(); }// Mount it to the prototype
            obj.__proto__ = arrayMethods
        }
        defineReactive(data)

        // View rendering
        function render() {
            app.innerHTML = data;
        }
        render();
       
    </script>
Copy the code

Changing the index and length of an array in Vue is not monitored. You need to modify the array through the above 7 variation methods to trigger the watcher of the array to update. An array of object types is also recursively hijacked.

If you want to change the index, you can do this via Vue.$set. The core code is the splice method