JavaScript剖析vue數據雙向綁定原理

1、數據雙向綁定的原理概述

Vue2.0

  Vue數據雙向綁定(即數據響應式),是利用了Object.defineProperty()這個方法從新定義了對象獲取屬性值get和設置屬性值set的操做來實現的。html

var obj  = {};
Object.defineProperty(obj, 'name', {
        get: function() {
            console.log('我被獲取了')
            return val;
        },
        set: function (newVal) {
            console.log('我被設置了')
        }
})
obj.name = 'zhangsan';//在給obj設置name屬性的時候,觸發了set這個方法
var val = obj.name;//在獲得obj的name屬性,會觸發get方法
複製代碼

Vue3.0

  Vue3.0版本中是採用ES6的Proxy對象來實現數據雙向綁定的。vue

2、數據雙向綁定的實現

  咱們先大體看下vue編譯解析的總體架構圖,以下圖所示: node

js實現簡單的數據雙向綁定

一、view視圖層建立html代碼
<div id="app">
    <h3>數據雙向綁定</h3>
    <div>
    	<div v-text="myText"></div>
    	<input type="text" v-model="myText"/>
    </div>
</div>
複製代碼
二、根據vue架構圖建立MVVM基本架構
// 發佈者
class Vue{
    constructor(options) {
    	this.options = options;
    	this.$data = options.data;
    	this.$el = document.querySelector(options.el);// #app 獲取app對象
    	this._directive = {};// 容器:存放訂閱者
    
    	this.Observe(this.$data);
    	this.Compile(this.$el);
    }
    // 劫持數據
    Observe(data) {}
    // 解析指令
    Compile(el) {}
}
// 訂閱者
class Watcher {
	
}

// 實例化Vue對象
var app = new Vue({
    el: '#app',
    data: {
    	myText: 'hello world!'
    }
})
複製代碼
三、而後實現MVVM中模型層(M)的數據綁定到視圖層(V)
// 發佈者
class Vue{
    constructor(options) {
    	this.options = options;
    	this.$data = options.data;
    	this.$el = document.querySelector(options.el);// #app 獲取app對象
    	this._directive = {};// 容器:存放訂閱者
    
    	this.Observe(this.$data);
    	this.Compile(this.$el);
    }
    // 劫持數據
    Observe(data) {
    	for (let key in data) {
    		this._directive[key] = []
    	}
    
    }
    // 解析指令
    Compile(el) {
    	let nodes = el.children;
    	for (let i = 0;i < nodes.length;i++) {
            let node = nodes[i];
            // 遞歸,把嵌套的元素都查找,看是否有指令
            if(node.children.length) {
            	this.Compile(node)
            }
            if(node.hasAttribute('v-text')) {
            	let attrVal = node.getAttribute('v-text');
            	this._directive[attrVal].push(new Watcher(node, this, attrVal, 'innerHTML'));
            }
            if(node.hasAttribute('v-model')) {
            	let attrVal = node.getAttribute('v-model');
            	this._directive[attrVal].push(new Watcher(node, this, attrVal, 'value'));
            }
    	}
    }
}
// 訂閱者
class Watcher {
    constructor(el, vm, exp, attr) {
    	this.el = el;// 元素對象 div input
    	this.vm = vm;
    	this.exp = exp;
    	this.attr = attr;
    
    	this.update();
    }
    update() {
    	this.el[this.attr] = this.vm.$data[this.exp];
    }
}
複製代碼
四、最後實現MVVM中視圖層(V)的數據更新綁定到模型層(M)
// 發佈者
class Vue{
    constructor(options) {
    	this.options = options;
    	this.$data = options.data;
    	this.$el = document.querySelector(options.el);// #app 獲取app對象
    	this._directive = {};// 容器:存放訂閱者
    
    	this.Observe(this.$data);
    	this.Compile(this.$el);
    }
    // 劫持數據
    Observe(data) {
    	for (let key in data) {
            this._directive[key] = [];
            
            let val = data[key];
            let watch = this._directive[key];
            Object.defineProperty(data, key, {
            	get: function() { 
            	    return val;
            	},
            	set: function(newVal) {
                    if(newVal !== val) {
                        val = newVal;
                        watch.forEach(element => {
                        	element.update()
                        })
                    }
            	}
            })
    	}
    }
    // 解析指令
    Compile(el) {
        let nodes = el.children;
        for (let i = 0;i < nodes.length;i++) {
            let node = nodes[i];
            // 遞歸,把嵌套的元素都查找,看是否有指令
            if(node.children.length) {
                this.Compile(node)
            }
            if(node.hasAttribute('v-text')) {
                let attrVal = node.getAttribute('v-text');
                this._directive[attrVal].push(new Watcher(node, this, attrVal, 'innerHTML'));
            }
            if(node.hasAttribute('v-model')) {
                let attrVal = node.getAttribute('v-model');
                this._directive[attrVal].push(new Watcher(node, this, attrVal, 'value'));
                
                node.addEventListener('input', () => {
                	this.$data[attrVal] = node.value;
                })
            }
        }
    }
}
// 訂閱者
class Watcher {
    constructor(el, vm, exp, attr) {
    	this.el = el;// 元素對象 div input
    	this.vm = vm;
    	this.exp = exp;
    	this.attr = attr;
    
    	this.update();
    }
    update() {
    	this.el[this.attr] = this.vm.$data[this.exp];
    }
}
複製代碼

小結

  經過js來實現數據雙向綁定,咱們就更深刻的瞭解Vue2.0的數據雙向綁定原理。但願本篇博客能夠給學習Vue的小夥伴帶來一些幫助!之後有機會再對Vue3.0 Proxy對象進行分析~~bash

相關文章
相關標籤/搜索