Vue implements two-way data binding

Vue implements two-way data binding

This article example shares the specific code of Vue to implement two-way data binding for your reference. The specific content is as follows

Arrays and objects in Vue use different binding methods

1. Vue object data binding

(1) Data detection

In js, we use Object.defineProperty() and ES6 proxy to detect objects

In vue2.x, Object.defineProperty() is used to detect data on objects. We first encapsulate Object.defineProperty with the following code:

function defineReactive(data, key, val){
    if(typeof val === 'object') new Observer(val)
    let dep = new Dep();
    Object.defineProperty(data, key, val) {
    enumerable: true,
    configurable: true,
    get: function () {
        dep.depend();
        return val;
    },
    set: function() {
        if(val === newVal) {
            return ;
        }
        val = newVal;
        dep.notify()
    }
}
}

The only parameters that need to be passed are data, key, and val. Get is triggered whenever data is read from the key in data, and set is triggered whenever data is modified.

(2) Dependency collection

Collect dependencies first, and when the properties change, trigger the collected dependencies in a loop again. Collect dependencies in getters and trigger dependencies in setters.

Dependency class Dep

export default class Dep {
    constructor() {
        this.subs = []
    }
    
    addSub() {
        this.subs.push(sub)
    }
    
    removeSub(sub) {
        remove(this.subs, sub)
    }
    
    depend() {
        if(window.target) {
            this.addSub(window.target)
        }
    }
    
    notify() {
        const subs = this.subs.slice()
        for(let i = 0, l = subs.length; i < l; i++) {
            subs[i].update()
        }
    }
}
 
function remove(arr, item) {
    if (arr.length) {
        const index = arr.indexOf(item)
        if(index > -1) {
            return arr.splice(index, 1)
        }
}
}

The watcher class is the dependency we collected

export default class Watcher {
    constructor(vm, expOrFn, cb) {
        this.vm = vm
        this.getter = parsePath(expOrFn)
        this.cb = cb
        this.value = this.get()
    }
    
    get() {
        window.target = this
        let value = this.getter.call(this.vm, this.vm)
        window.target = undefined
        return value
    }
    
    update() {
        const oldValue = this.value
        this.value = this.get()
        this.cb.call(this.vm, this.value, oldValue)
    }
}

(3) Recursively detect all keys (Observer)

export class Observer {
    constructor(value) {
        this.value = value;
        
        if(!Array.isArray(value)) {
            this.walk(value)
        }
    }
    walk(obj) {
        const keys = Object.keys(obj)
        for(let i = 0; i < keys.length; i++) {
            defineReactive(obj, keys[i], obj[keys[i]])
        }
    }
}

The Observer class makes all the properties of the object responsive

2.Array change detection

(1) Tracking changes in arrays uses interceptors to override prototype methods

const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto);
// The same object as the array prototype as an interceptor ['push','pop','shift','unshift','splice','sort','reverse'].forEach(function (method) {
    const original = arrayProto[method]
    Object.defineProperty(arrayMethods, method, {
        value: function mutator(...args) {
              return original.apply(this, args)
        },
        enumerable: false,
        writable: true,
        configurable: true
    })
})

The interceptor override prototype has only one sentence

value.__proto__ = arrayMethods

If there is no __proto__ property, Vue will mount these arrayMethods to the detected array

Arrays are similar to objects in that they collect dependencies in getters, while array-triggered dependencies are in interceptors.

The array's dependencies are stored on the Observer instance and must be accessible to both getters and interceptors.

__ob__ is a non-enumerable attribute, the value of this attribute is the current Observer instance

Save the Dep instance in the Observer attribute. If the value already has an __ob__ attribute, there is no need to create an Observer instance again to avoid repeated detection of value changes.

Send notifications like array dependencies

this.__ob__.dep.notify();

(2) Specific methods for detecting data changes

Loop through each item in the array and execute the observe function to detect changes

observeArray(items) {
    for(let i = 0; l = items.length; i < l; i++) {
        observe(items[i]);    
    }
}

Arrays need to detect new elements

By intercepting push, unshift, splice and other methods, and storing args in inserted

if(inserted) ob.observeArray(inserted)

Summarize:

Array tracks changes differently than Object does, so we create an interceptor to overwrite the array prototype to track changes. In order not to pollute the global Array.prototype, we only use __proto__ in Observer to overwrite the prototype for the array that needs to detect changes.

Array collects dependencies in the same way as Object, which is collected in getters, triggered in interceptors, and dependencies are stored on Observer instances. On the Observer, we mark each detected data with __ob__ and save this(Observer) on __ob__. This is mainly to ensure that the same data is only detected once. In addition, we can easily get the dependencies saved on the Observer instance through __ob__. The array needs to loop to make each array item responsive. When a new element is added to the array, we extract the parameters and then use observeArray to detect changes in the new data. For arrays, only prototype methods can be intercepted, and some unique methods cannot be intercepted.

The above is the full content of this article. I hope it will be helpful for everyone’s study. I also hope that everyone will support 123WORDPRESS.COM.

You may also be interested in:
  • Vue mvvm data response implementation
  • Explanation of mvvm mode in vue
  • Detailed explanation of Vue.js template syntax
  • Analysis of the principles of Vue data binding
  • Vue basics MVVM, template syntax and data binding

<<:  In-depth analysis of JDBC and MySQL temporary tablespace

>>:  What command is better for fuzzy searching files in Linux?

Recommend

Summary of the dockerfile-maven-plugin usage guide

Table of contents pom configuration Setting.xml c...

Detailed process of decompressing and installing mysql5.7.17 zip

1. Download address https://dev.mysql.com/downloa...

Solution to mysql failure to start due to insufficient disk space in ubuntu

Preface Recently, I added two fields to a table i...

Detailed tutorial on Tomcat installation and deployment in Windows 10

Table of contents 1 Java environment configuratio...

JavaScript implements checkbox selection function

This article example shares the specific code of ...

In-depth explanation of MySQL learning engine, explain and permissions

engine Introduction Innodb engine The Innodb engi...

JavaScript source code for Elimination

JavaScript to achieve the source code download ad...

MySQL backup table operation based on Java

The core is mysqldump and Runtime The operation i...

CSS3+Bezier curve to achieve scalable input search box effect

Without further ado, here are the renderings. The...

How to use less in WeChat applet (optimal method)

Preface I am used to writing less/sass, but now I...

Summary of learning HTML tags and basic elements

1. Elements and tags in HTML <br />An eleme...

Native JavaScript implementation of progress bar

The specific code for JavaScript to implement the...

HTML tutorial, HTML default style

html , address , blockquote , body , dd , div , d...

Tutorial on installing VMWare15.5 under Linux

To install VMWare under Linux, you need to downlo...

Detailed steps to install xml extension in php under linux

Installing XML extension in PHP Linux 1. Enter th...