I’ve been using VUE for a long time, and one of the features of VUE is how bidirectional binding works, which is basically hijacking properties and modifying them via the set and get methods of Object.defineProperty(). This outbreak at home nothing, read a lot of source code in this area, so I plan to write a brief version of two-way binding. If you don’t think well, if you find any problems, please come forward and study together.

Implementation approach

1. You need an Oberser that listens for all data and attribute changes and notifies subscribers of any changes

2. Compile a command to scan nodes, initialize templates, and update views

3. You need a method that Watcher accepts the changes from Oberser and then executes the response

oberser

Vue.prototype.oberser = function (obj) { let that = this Object.keys(obj).forEach(key => { var value = obj[key]; DefineProperty (THX. Data,key, {64x: true, different) {} Get () {return value; }, set(newVal) {// Set value = newVal; Tha.pool.notice () // Execute method update view}})})}Copy the code

Here we use forEach directly to loop through the incoming data, and then we can use Object.defineProperty to get the data and set the new data.

compile

Vue.prototype.compile = function (el) { let that = this,nodes = el.children; for(let i = 0; i<nodes.length; i++) { if(nodes[i].hasAttribute('v-model') && nodes[i].tagName == 'INPUT'){ Nodes [I].adDeventListener ('input',(function(key) {var attVal = nodes[I].getAttribute('v-model'); that.Pool.add( new Watcher( nodes[i], that, attVal, 'value' ) ) return function () { that.data[attVal] = nodes[key].value; Set => Set triggers watch update view}})(I))} if (Nodes [I].hasattribute ('v-bind')) {// v-bind var attVal =  nodes[i].getAttribute('v-bind'); Add (new Watcher(Nodes [I], that, attVal, 'innerHTML'))}}}Copy the code

The child node in the loop app, if it’s an input field and it has a V-modle, gives the input field a listening event, gets the value, and adds a Watcher to the subscription pool, which is then used to update the input field. If the node has v-bind, we add a watcher to the subscription pool, but this time we change it to innerHTML, which is then used to change the text

Watcher

 function Watcher(el,vm,val,attr) {
 
        this.el  =el;
        this.vm = vm;
        this.val = val;
        this.attr = attr;
        this.update();
        }
    Watcher.prototype.update = function () {
        this.el[this.attr] = this.vm.data[this.val]
    }
        
Copy the code

Above is a watcher that receives the data from compile. Watcher has an update method that updates the page based on the incoming nodes

function Pool() { this.subs = [], this.add(), this.notice() } Pool.prototype.add = function (sub) { if(sub) { this.subs.push(sub) } } Pool.prototype.notice = function  () { this.subs.forEach(item => { item.update() }) }Copy the code

This is a subscription pool that collects all the watchers and has two properties, one is to add new watcher and the other is to loop through the update method of watcher

conclusion

Finally, all functions need to be placed into a main function after the page loads.

Onload = function () {var app = new Vue({el: '#app', data: {data1: '888888' } }) } function Vue(config = {}) { this.watcher = {} this.data = config.data this.Pool = new Pool() this.oberser(config.data) this.compile(document.querySelector(config.el)) }Copy the code

Such as multi-layer nesting can not be used, but the energy is limited, can only be here first.

reference

Juejin. Cn/post / 684490…