理解Proxy及使用Proxy實(shí)現(xiàn)vue數(shù)據(jù)雙向綁定操作
1.什么是Proxy?它的作用是?
據(jù)阮一峰文章介紹:Proxy可以理解成,在目標(biāo)對(duì)象之前架設(shè)一層 "攔截",當(dāng)外界對(duì)該對(duì)象訪問的時(shí)候,都必須經(jīng)過這層攔截,而Proxy就充當(dāng)了這種機(jī)制,類似于代理的含義,它可以對(duì)外界訪問對(duì)象之前進(jìn)行過濾和改寫該對(duì)象。
如果對(duì)vue2.xx了解或看過源碼的人都知道,vue2.xx中使用 Object.defineProperty()方法對(duì)該對(duì)象通過 遞歸+遍歷的方式來實(shí)現(xiàn)對(duì)數(shù)據(jù)的監(jiān)控的,具體了解
Object.defineProperty可以看我上一篇文章(http://www.dhdzp.com/article/191097.htm). 但是通過上一篇Object.defineProperty文章 我們也知道,當(dāng)我們使用數(shù)組的方法或改變數(shù)組的下標(biāo)是不能重新觸發(fā) Object.defineProperty中的set()方法的,因此就做不到實(shí)時(shí)響應(yīng)了。所以使用 Object.defineProperty 存在如下缺點(diǎn):
1. 監(jiān)聽數(shù)組的方法不能觸發(fā)Object.defineProperty方法中的set操作(如果要監(jiān)聽的到話,需要重新編寫數(shù)組的方法)。
2. 必須遍歷每個(gè)對(duì)象的每個(gè)屬性,如果對(duì)象嵌套很深的話,需要使用遞歸調(diào)用。
因此vue3.xx中之后就改用Proxy來更好的解決如上面的問題。在學(xué)習(xí)使用Proxy實(shí)現(xiàn)數(shù)據(jù)雙向綁定之前,我們還是一步步來,先學(xué)習(xí)了Proxy基本知識(shí)點(diǎn)。
Proxy基本語法
const obj = new Proxy(target, handler);
參數(shù)說明如下:
target: 被代理對(duì)象。
handler: 是一個(gè)對(duì)象,聲明了代理target的一些操作。
obj: 是被代理完成之后返回的對(duì)象。
但是當(dāng)外界每次對(duì)obj進(jìn)行操作時(shí),就會(huì)執(zhí)行handler對(duì)象上的一些方法。handler中常用的對(duì)象方法如下:
1. get(target, propKey, receiver)
2. set(target, propKey, value, receiver)
3. has(target, propKey)
4. construct(target, args):
5. apply(target, object, args)
如上是Proxy中handler 對(duì)象的方法,其實(shí)它和Reflect里面的方法類似的,想要了解Reflect看這篇文章
如下代碼演示:
const target = {
name: 'kongzhi'
};
const handler = {
get: function(target, key) {
console.log(`${key} 被讀取`);
return target[key];
},
set: function(target, key, value) {
console.log(`${key} 被設(shè)置為 ${value}`);
target[key] = value;
}
};
const testObj = new Proxy(target, handler);
/*
獲取testObj中name屬性值
會(huì)自動(dòng)執(zhí)行 get函數(shù)后 打印信息:name 被讀取 及輸出名字 kongzhi
*/
console.log(testObj.name);
/*
改變target中的name屬性值
打印信息如下: name 被設(shè)置為 111
*/
testObj.name = 111;
console.log(target.name); // 輸出 111
如上代碼所示:也就是說 target是被代理的對(duì)象,handler是代理target的,那么handler上面有set和get方法,當(dāng)每次打印target中的name屬性值的時(shí)候會(huì)自動(dòng)執(zhí)行handler中g(shù)et函數(shù)方法,當(dāng)每次設(shè)置 target.name 屬性值的時(shí)候,會(huì)自動(dòng)調(diào)用 handler中的set方法,因此target對(duì)象對(duì)應(yīng)的屬性值會(huì)發(fā)生改變,同時(shí)改變后的 testObj對(duì)象也會(huì)發(fā)生改變。同理改變返回后 testObj對(duì)象中的屬性也會(huì)改變?cè)瓕?duì)象target的屬性的,因?yàn)閷?duì)象是引用類型的,是同一個(gè)引用的。如果這樣還是不好理解的話,可以簡(jiǎn)單的看如下代碼應(yīng)該可以理解了:
const target = {
name: 'kongzhi'
};
const testA = target;
testA.name = 'xxx';
console.log(testA.name); // 打印 xxx
console.log(target.name); // 打印 xxx
2.get(target, propKey, receiver)
該方法的含義是:用于攔截某個(gè)屬性的讀取操作。它有三個(gè)參數(shù),如下解析:
target: 目標(biāo)對(duì)象。
propKey: 目標(biāo)對(duì)象的屬性。
receiver: (可選),該參數(shù)為上下文this對(duì)象
如下代碼演示:
const obj = {
name: 'kongzhi'
};
const handler = {
get: function(target, propKey) {
// 使用 Reflect來判斷該目標(biāo)對(duì)象是否有該屬性
if (Reflect.has(target, propKey)) {
// 使用Reflect 來讀取該對(duì)象的屬性
return Reflect.get(target, propKey);
} else {
throw new ReferenceError('該目標(biāo)對(duì)象沒有該屬性');
}
}
};
const testObj = new Proxy(obj, handler);
/*
Proxy中讀取某個(gè)對(duì)象的屬性值的話,
就會(huì)使用get方法進(jìn)行攔截,然后返回該值。
*/
console.log(testObj.name); // kongzhi
/*
如果對(duì)象沒有該屬性的話,就會(huì)進(jìn)入else語句,就會(huì)報(bào)錯(cuò):
Uncaught ReferenceError: 該目標(biāo)對(duì)象沒有該屬性
*/
// console.log(testObj.name2);
/*
其實(shí)Proxy中攔截的操作是在原型上的,因此我們也可以使用 Object.create(obj)
來實(shí)現(xiàn)對(duì)象的繼承的。
如下代碼演示:
*/
const testObj2 = Object.create(testObj);
console.log(testObj2.name);
// 看看他們的原型是否相等
console.log(testObj2.__proto__ === testObj.__proto__); // 返回true
如果沒有這個(gè)攔截的話,如果某個(gè)對(duì)象沒有該屬性的話,會(huì)輸出 undefined.
3.set(target, propKey, value, receiver)
該方法是用來攔截某個(gè)屬性的賦值操作,它可以接受四個(gè)參數(shù),參數(shù)解析分別如下:
target: 目標(biāo)對(duì)象。
propKey: 目標(biāo)對(duì)象的屬性名
value: 屬性值
receiver(可選): 一般情況下是Proxy實(shí)列
如下代碼演示:
const obj = {
'name': 'kongzhi'
};
const handler = {
set: function(obj, prop, value) {
return Reflect.set(obj, prop, value);
}
};
const proxy = new Proxy(obj, handler);
proxy.name = '我是空智';
console.log(proxy.name); // 輸出: 我是空智
console.log(obj); // 輸出: {name: '我是空智'}
當(dāng)然如果設(shè)置該對(duì)象的屬性是不可寫的,那么set方法就不起作用了,如下代碼演示:
const obj = {
'name': 'kongzhi'
};
Object.defineProperty(obj, 'name', {
writable: false
});
const handler = {
set: function(obj, prop, value, receiver) {
Reflect.set(obj, prop, value);
}
};
const proxy = new Proxy(obj, handler);
proxy.name = '我是空智';
console.log(proxy.name); // 打印的是 kongzhi
注意:proxy對(duì)數(shù)組也是可以監(jiān)聽的;如下代碼演示,數(shù)組中的 push方法監(jiān)聽:
const obj = [{
'name': 'kongzhi'
}];
const handler = {
set: function(obj, prop, value) {
return Reflect.set(obj, prop, value);
}
};
const proxy = new Proxy(obj, handler);
proxy.push({'name': 'kongzhi222'});
proxy.forEach(function(item) {
console.log(item.name); // 打印出 kongzhi kongzhi222
});
4.has(target, propKey)
該方法是判斷某個(gè)目標(biāo)對(duì)象是否有該屬性名。接收二個(gè)參數(shù),分別為目標(biāo)對(duì)象和屬性名。返回的是一個(gè)布爾型。
如下代碼演示:
const obj = {
'name': 'kongzhi'
};
const handler = {
has: function(target, key) {
if (Reflect.has(target, key)) {
return true;
} else {
return false;
}
}
};
const proxy = new Proxy(obj, handler);
console.log(Reflect.has(obj, 'name')); // true
console.log(Reflect.has(obj, 'age')); // false
5.construct(target, args, newTarget):
該方法是用來攔截new命令的,它接收三個(gè)參數(shù),分別為 目標(biāo)對(duì)象,構(gòu)造函數(shù)的參數(shù)對(duì)象及創(chuàng)造實(shí)列的對(duì)象。
第三個(gè)參數(shù)是可選的。它的作用是攔截對(duì)象屬性。
如下代碼演示:
function A(name) {
this.name = name;
}
const handler = {
construct: function(target, args, newTarget) {
/*
輸出: function A(name) {
this.name = name;
}
*/
console.log(target);
// 輸出: ['kongzhi', {age: 30}]
console.log(args);
return args
}
};
const Test = new Proxy(A, handler);
const obj = new Test('kongzhi', {age: 30});
console.log(obj); // 輸出: ['kongzhi', {age: 30}]
6.apply(target, object, args)
該方法是攔截函數(shù)的調(diào)用的。該方法接收三個(gè)參數(shù),分別是目標(biāo)對(duì)象。目標(biāo)對(duì)象上下文this對(duì)象 和 目標(biāo)對(duì)象的數(shù)組;它和 Reflect.apply參數(shù)是一樣的,了解 Reflect.apply(http://www.dhdzp.com/article/191099.htm).
使用demo如下演示:
function testA(p1, p2) {
return p1 + p2;
}
const handler = {
apply: function(target, ctx, args) {
/*
這里的 ...arguments 其實(shí)就是上面的三個(gè)參數(shù) target, ctx, args 對(duì)應(yīng)的值。
分別為:
target: function testA(p1, p2) {
return p1 + p2;
}
ctx: undefined
args: [1, 2]
使用 Reflect.apply(...arguments) 調(diào)用testA函數(shù),因此返回 (1+2) * 2 = 6
*/
console.log(...arguments);
return Reflect.apply(...arguments) * 2;
}
}
const proxy = new Proxy(testA, handler);
console.log(proxy(1, 2)); // 6
// 也可以如下調(diào)用
console.log(proxy.apply(null, [1, 3])); // 8
// 我們也可以使用 Reflect.apply 調(diào)用
console.log(Reflect.apply(proxy, null, [3, 5])); // 16
7.使用Proxy實(shí)現(xiàn)簡(jiǎn)單的vue雙向綁定
vue3.x使用了Proxy來對(duì)數(shù)據(jù)進(jìn)行監(jiān)聽了,因此我們來簡(jiǎn)單的來學(xué)習(xí)下使用Proxy來實(shí)現(xiàn)一個(gè)簡(jiǎn)單的vue雙向綁定。
我們都知道實(shí)現(xiàn)數(shù)據(jù)雙向綁定,需要實(shí)現(xiàn)如下幾點(diǎn):
1. 需要實(shí)現(xiàn)一個(gè)數(shù)據(jù)監(jiān)聽器 Observer, 能夠?qū)λ袛?shù)據(jù)進(jìn)行監(jiān)聽,如果有數(shù)據(jù)變動(dòng)的話,拿到最新的值并通知訂閱者Watcher.
2. 需要實(shí)現(xiàn)一個(gè)指令解析器Compile,它能夠?qū)γ總€(gè)元素的指令進(jìn)行掃描和解析,根據(jù)指令模板替換數(shù)據(jù),以及綁定相對(duì)應(yīng)的函數(shù)。
3. 需要實(shí)現(xiàn)一個(gè)Watcher, 它是鏈接Observer和Compile的橋梁,它能夠訂閱并收到每個(gè)屬性變動(dòng)的通知,然后會(huì)執(zhí)行指令綁定的相對(duì)應(yīng)
的回調(diào)函數(shù),從而更新視圖。
下面是一個(gè)簡(jiǎn)單的demo源碼如下(我們可以參考下,理解下原理):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>標(biāo)題</title>
</head>
<body>
<div id="app">
<input type="text" v-model='count' />
<input type="button" value="增加" @click="add" />
<input type="button" value="減少" @click="reduce" />
<div v-bind="count"></div>
</div>
<script type="text/javascript">
class Vue {
constructor(options) {
this.$el = document.querySelector(options.el);
this.$methods = options.methods;
this._binding = {};
this._observer(options.data);
this._compile(this.$el);
}
_pushWatcher(watcher) {
if (!this._binding[watcher.key]) {
this._binding[watcher.key] = [];
}
this._binding[watcher.key].push(watcher);
}
/*
observer的作用是能夠?qū)λ械臄?shù)據(jù)進(jìn)行監(jiān)聽操作,通過使用Proxy對(duì)象
中的set方法來監(jiān)聽,如有發(fā)生變動(dòng)就會(huì)拿到最新值通知訂閱者。
*/
_observer(datas) {
const me = this;
const handler = {
set(target, key, value) {
const rets = Reflect.set(target, key, value);
me._binding[key].map(item => {
item.update();
});
return rets;
}
};
this.$data = new Proxy(datas, handler);
}
/*
指令解析器,對(duì)每個(gè)元素節(jié)點(diǎn)的指令進(jìn)行掃描和解析,根據(jù)指令模板替換數(shù)據(jù),以及綁定相對(duì)應(yīng)的更新函數(shù)
*/
_compile(root) {
const nodes = Array.prototype.slice.call(root.children);
const data = this.$data;
nodes.map(node => {
if (node.children && node.children.length) {
this._compile(node.children);
}
const $input = node.tagName.toLocaleUpperCase() === "INPUT";
const $textarea = node.tagName.toLocaleUpperCase() === "TEXTAREA";
const $vmodel = node.hasAttribute('v-model');
// 如果是input框 或 textarea 的話,并且?guī)в?v-model 屬性的
if (($vmodel && $input) || ($vmodel && $textarea)) {
const key = node.getAttribute('v-model');
this._pushWatcher(new Watcher(node, 'value', data, key));
node.addEventListener('input', () => {
data[key] = node.value;
});
}
if (node.hasAttribute('v-bind')) {
const key = node.getAttribute('v-bind');
this._pushWatcher(new Watcher(node, 'innerHTML', data, key));
}
if (node.hasAttribute('@click')) {
const methodName = node.getAttribute('@click');
const method = this.$methods[methodName].bind(data);
node.addEventListener('click', method);
}
});
}
}
/*
watcher的作用是 鏈接Observer 和 Compile的橋梁,能夠訂閱并收到每個(gè)屬性變動(dòng)的通知,
執(zhí)行指令綁定的響應(yīng)的回調(diào)函數(shù),從而更新視圖。
*/
class Watcher {
constructor(node, attr, data, key) {
this.node = node;
this.attr = attr;
this.data = data;
this.key = key;
}
update() {
this.node[this.attr] = this.data[this.key];
}
}
</script>
<script type="text/javascript">
new Vue({
el: '#app',
data: {
count: 0
},
methods: {
add() {
this.count++;
},
reduce() {
this.count--;
}
}
});
</script>
</body>
</html>
如上代碼我們來分析下原理如下:
首先他是使用ES6編寫的語法來實(shí)現(xiàn)的。首先我們想實(shí)現(xiàn)類似vue那要的初始化代碼,如下這樣設(shè)想:
new Vue({
el: '#app',
data: {
count: 0
},
methods: {
add() {
this.count++;
},
reduce() {
this.count--;
}
}
});
因此使用ES6 基本語法如下:
class Vue {
constructor(options) {
this.$el = document.querySelector(options.el);
this.$methods = options.methods;
this._binding = {};
this._observer(options.data);
this._compile(this.$el);
}
}
Vue類使用new創(chuàng)建一個(gè)實(shí)例化的時(shí)候,就會(huì)執(zhí)行 constructor方法代碼,因此options是vue傳入的一個(gè)對(duì)象,它有 el,data, methods等屬性。 如上代碼先執(zhí)行 this._observer(options.data); 該 observer 函數(shù)就是監(jiān)聽所有數(shù)據(jù)的變動(dòng)函數(shù)。基本代碼如下:
1. 實(shí)現(xiàn)Observer對(duì)所有的數(shù)據(jù)進(jìn)行監(jiān)聽。
_observer(datas) {
const me = this;
const handler = {
set(target, key, value) {
const rets = Reflect.set(target, key, value);
me._binding[key].map(item => {
item.update();
});
return rets;
}
};
this.$data = new Proxy(datas, handler);
}
使用了我們上面介紹的Proxy中的set方法對(duì)所有的數(shù)據(jù)進(jìn)行監(jiān)聽,只要我們Vue實(shí)列屬性data中有任何數(shù)據(jù)發(fā)生改變的話,都會(huì)自動(dòng)調(diào)用Proxy中的set方法,我們上面的代碼使用了 const rets = Reflect.set(target, key, value); return rets; 這樣的代碼,就是對(duì)我們的data中的任何數(shù)據(jù)發(fā)生改變后,使用該方法重新設(shè)置新值,然后返回給 this.$data保存到這個(gè)全局里面。
me._binding[key].map(item => {
item.update();
});
如上this._binding 是一個(gè)對(duì)象,對(duì)象里面保存了所有的指令及對(duì)應(yīng)函數(shù),如果發(fā)生改變,拿到最新值通知訂閱者,因此通知Watcher類中的update方法,如下Watcher類代碼如下:
/*
watcher的作用是 鏈接Observer 和 Compile的橋梁,能夠訂閱并收到每個(gè)屬性變動(dòng)的通知,
執(zhí)行指令綁定的響應(yīng)的回調(diào)函數(shù),從而更新視圖。
*/
class Watcher {
constructor(node, attr, data, key) {
this.node = node;
this.attr = attr;
this.data = data;
this.key = key;
}
update() {
this.node[this.attr] = this.data[this.key];
}
}
2. 實(shí)現(xiàn)Compile
如下代碼初始化
class Vue {
constructor(options) {
this.$el = document.querySelector(options.el);
this._compile(this.$el);
}
}
_compile 函數(shù)的作用就是對(duì)頁面中每個(gè)元素節(jié)點(diǎn)的指令進(jìn)行解析和掃描的,根據(jù)指令模板替換數(shù)據(jù),以及綁定相應(yīng)的更新函數(shù)。
代碼如下:
_compile(root) {
const nodes = Array.prototype.slice.call(root.children);
const data = this.$data;
nodes.map(node => {
if (node.children && node.children.length) {
this._compile(node.children);
}
const $input = node.tagName.toLocaleUpperCase() === "INPUT";
const $textarea = node.tagName.toLocaleUpperCase() === "TEXTAREA";
const $vmodel = node.hasAttribute('v-model');
// 如果是input框 或 textarea 的話,并且?guī)в?v-model 屬性的
if (($vmodel && $input) || ($vmodel && $textarea)) {
const key = node.getAttribute('v-model');
this._pushWatcher(new Watcher(node, 'value', data, key));
node.addEventListener('input', () => {
data[key] = node.value;
});
}
if (node.hasAttribute('v-bind')) {
const key = node.getAttribute('v-bind');
this._pushWatcher(new Watcher(node, 'innerHTML', data, key));
}
if (node.hasAttribute('@click')) {
const methodName = node.getAttribute('@click');
const method = this.$methods[methodName].bind(data);
node.addEventListener('click', method);
}
});
}
}
如上代碼,
1. 拿到根元素的子節(jié)點(diǎn),然后讓子元素變成數(shù)組的形式,如代碼:
const nodes = Array.prototype.slice.call(root.children);
2. 保存變動(dòng)后的 this.$data, 如下代碼:
const data = this.$data;
3. nodes子節(jié)點(diǎn)進(jìn)行遍歷,如果改子節(jié)點(diǎn)還有子節(jié)點(diǎn)的話,就會(huì)遞歸調(diào)用 _compile方法,如下代碼:
nodes.map(node => {
if (node.children && node.children.length) {
this._compile(node.children);
}
});
4. 對(duì)子節(jié)點(diǎn)進(jìn)行判斷,如果子節(jié)點(diǎn)是input元素或textarea元素的話,并且有 v-model這樣的指令的話,如下代碼:
nodes.map(node => {
const $input = node.tagName.toLocaleUpperCase() === "INPUT";
const $textarea = node.tagName.toLocaleUpperCase() === "TEXTAREA";
const $vmodel = node.hasAttribute('v-model');
// 如果是input框 或 textarea 的話,并且?guī)в?v-model 屬性的
if (($vmodel && $input) || ($vmodel && $textarea)) {
const key = node.getAttribute('v-model');
this._pushWatcher(new Watcher(node, 'value', data, key));
node.addEventListener('input', () => {
data[key] = node.value;
});
}
});
如上代碼,如果有 v-model,就獲取v-model該屬性值,如代碼:
const key = node.getAttribute('v-model');
然后把該指令通知訂閱者 Watcher; 如下代碼:
this._pushWatcher(new Watcher(node, 'value', data, key));
就會(huì)調(diào)用 Watcher類的constructor的方法,如下代碼:
class Watcher {
constructor(node, attr, data, key) {
this.node = node;
this.attr = attr;
this.data = data;
this.key = key;
}
}
把 node節(jié)點(diǎn),attr屬性,data數(shù)據(jù),v-model指令key保存到this對(duì)象中了。然后調(diào)用 this._pushWatcher(watcher); 這樣方法。
_pushWatcher代碼如下:
if (!this._binding[watcher.key]) {
this._binding[watcher.key] = [];
}
this._binding[watcher.key].push(watcher);
如上代碼,先判斷 this._binding 有沒有 v-model指令中的key, 如果沒有的話,就把該 this._binding[key] = []; 設(shè)置成空數(shù)組。然后就把它存入 this._binding[key] 數(shù)組里面去。
5. 對(duì)于 input 或 textarea 這樣的 v-model 會(huì)綁定相對(duì)應(yīng)的函數(shù),如下代碼:
node.addEventListener('input', () => {
data[key] = node.value;
});
當(dāng)input或textarea有值發(fā)生改變的話,那么就把最新的值存入 Vue類中的data對(duì)象里面去,因此data中的數(shù)據(jù)會(huì)發(fā)生改變,因此會(huì)自動(dòng)觸發(fā)執(zhí)行 _observer 函數(shù)中的Proxy中的set方法函數(shù),還是一樣,首先更新最新值,使用代碼:
const rets = Reflect.set(target, key, value);
然后遍歷 保存到 this._binding 對(duì)象中對(duì)應(yīng)的鍵;如下代碼:
me._binding[key].map(item => {
console.log(item);
item.update();
});
如上,我們?cè)趇nput輸入框輸入1的時(shí)候,打印item值如下所示:

然后執(zhí)行 item.update()方法,update方法如下:
class Watcher {
update() {
this.node[this.attr] = this.data[this.key];
}
}
就會(huì)更新值到視圖里面去,比如input或textarea, 那么 attr = 'value', node 是該元素的節(jié)點(diǎn),key 就是 v-model中的屬性值,因此 this.node['value'] = this.data[key];
然后同時(shí)代碼中如果有 v-bind這樣的指令的話,也會(huì)和上面的邏輯一樣判斷和執(zhí)行;如下 v-bind指令代碼如下:
if (node.hasAttribute('v-bind')) {
const key = node.getAttribute('v-bind');
this._pushWatcher(new Watcher(node, 'innerHTML', data, key));
}
然后也會(huì)更新到視圖里面去,那么 attr = 'innerHTML', node 是該元素的節(jié)點(diǎn),key 也是 v-model中的屬性值了,因此 this.node.innerHTML = thid.data['key'];
比如頁面中html代碼如下:
<div id="app"> <input type="text" v-model='count' /> <input type="button" value="增加" @click="add" /> <input type="button" value="減少" @click="reduce" /> <div v-bind="count"></div> </div>
實(shí)列化代碼如下:
new Vue({
el: '#app',
data: {
count: 0
},
methods: {
add() {
this.count++;
},
reduce() {
this.count--;
}
}
});
因此上面的 node 是 <input type="text" v-model='count' /> input中的node節(jié)點(diǎn)了,因此 node.value = this.data['count']; 因此 input框的值就更新了,同時(shí) <div v-bind="count"></div> 該節(jié)點(diǎn)通過 node.innerHTML = this.data['count'] 這樣的話,值也得到了更新了。
6. 對(duì)于頁面中元素節(jié)點(diǎn)帶有 @click這樣的方法,也有判斷,如下代碼:
if (node.hasAttribute('@click')) {
const methodName = node.getAttribute('@click');
const method = this.$methods[methodName].bind(data);
node.addEventListener('click', method);
}
如上代碼先判斷該node是否有該屬性,然后獲取該屬性的值,比如html頁面中有 @click="add" 和 @click="reduce" 這樣的,當(dāng)點(diǎn)擊的時(shí)候,也會(huì)調(diào)用 this.methods[methodName].bind(data)中對(duì)應(yīng)vue實(shí)列中對(duì)應(yīng)的函數(shù)的。因此也會(huì)執(zhí)行函數(shù)的,其中data就是this.data,監(jiān)聽該對(duì)象的值發(fā)生改變的話,同樣會(huì)調(diào)用 Proxy中的set函數(shù),最后也是一樣執(zhí)行函數(shù)去更新視圖的。如上就是使用proxy實(shí)現(xiàn)數(shù)據(jù)雙向綁定的基本原理的。
以上這篇理解Proxy及使用Proxy實(shí)現(xiàn)vue數(shù)據(jù)雙向綁定操作就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- 解決vue項(xiàng)目input輸入框雙向綁定數(shù)據(jù)不實(shí)時(shí)生效問題
- Vue數(shù)據(jù)雙向綁定原理實(shí)例解析
- vue子組件改變父組件傳遞的prop值通過sync實(shí)現(xiàn)數(shù)據(jù)雙向綁定(DEMO)
- Vue數(shù)據(jù)雙向綁定底層實(shí)現(xiàn)原理
- vue雙向綁定數(shù)據(jù)限制長(zhǎng)度的方法
- 詳解基于Vue的支持?jǐn)?shù)據(jù)雙向綁定的select組件
- vue響應(yīng)式更新機(jī)制及不使用框架實(shí)現(xiàn)簡(jiǎn)單的數(shù)據(jù)雙向綁定問題
- 詳解vue中的父子傳值雙向綁定及數(shù)據(jù)更新問題
- Vuejs學(xué)習(xí)筆記之使用指令v-model完成表單的數(shù)據(jù)雙向綁定
- Vue使用.sync 實(shí)現(xiàn)父子組件的雙向綁定數(shù)據(jù)問題
- vue自定v-model實(shí)現(xiàn)表單數(shù)據(jù)雙向綁定問題
- vue 數(shù)據(jù)雙向綁定的實(shí)現(xiàn)方法
相關(guān)文章
Vue時(shí)間軸 vue-light-timeline的用法說明
這篇文章主要介紹了Vue時(shí)間軸 vue-light-timeline的用法說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-10-10
Vue.js中使用Vuex實(shí)現(xiàn)組件數(shù)據(jù)共享案例
這篇文章主要介紹了Vue.js中使用Vuex實(shí)現(xiàn)組件數(shù)據(jù)共享案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-07-07
Vue編譯報(bào)錯(cuò)內(nèi)存溢出問題解決方式
這篇文章主要為大家介紹了Vue編譯報(bào)錯(cuò)內(nèi)存溢出問題解決方式,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
preload對(duì)比prefetch的功能區(qū)別詳解
這篇文章主要為大家介紹了preload對(duì)比prefetch的使用區(qū)別詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
解讀Vue3中keep-alive和動(dòng)態(tài)組件的實(shí)現(xiàn)邏輯
這篇文章主要介紹了Vue3中keep-alive和動(dòng)態(tài)組件的實(shí)現(xiàn)邏輯,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05
vue實(shí)現(xiàn)修改標(biāo)簽中的內(nèi)容:id class style
這篇文章主要介紹了vue實(shí)現(xiàn)修改標(biāo)簽中的內(nèi)容:id class style,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
vue中使用 pinia 全局狀態(tài)管理的實(shí)現(xiàn)
本文主要介紹了vue中使用 pinia 全局狀態(tài)管理的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07

