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

A brief analysis of the responsiveness principle and differences of Vue2.0/3.0

Preface Since vue3.0 was officially launched, man...

The best way to solve the 1px border on mobile devices (recommended)

When developing for mobile devices, you often enc...

MySQL uses binlog logs to implement data recovery

MySQL binlog is a very important log in MySQL log...

HTML Grammar Encyclopedia_HTML Language Grammar Encyclopedia (Must Read)

Volume Label, Property Name, Description 002 <...

Detailed explanation of non-parent-child component communication in Vue3

Table of contents First method App.vue Home.vue H...

js canvas realizes slider verification

This article example shares the specific code of ...

Tomcat8 uses cronolog to split Catalina.Out logs

background If the catalina.out log file generated...

Detailed explanation of Mysql transaction processing

1. MySQL transaction concept MySQL transactions a...

Future-oriented all-round web design: progressive enhancement

<br />Original: Understanding Progressive En...

Implementation of webpack code fragmentation

Table of contents background CommonsChunkPlugin s...