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版本中是採用ES6的Proxy對象來實現數據雙向綁定的。vue
咱們先大體看下vue編譯解析的總體架構圖,以下圖所示: node
<div id="app">
<h3>數據雙向綁定</h3>
<div>
<div v-text="myText"></div>
<input type="text" v-model="myText"/>
</div>
</div>
複製代碼
// 發佈者
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!'
}
})
複製代碼
// 發佈者
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];
}
}
複製代碼
// 發佈者
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