1. 1什么是Proxy?
Proxy直譯過(guò)來(lái)就是代理的意思, Proxy對(duì)象用來(lái)定義基本操作的自定義行為(如屬性查找,枚舉,賦值,函數(shù)調(diào)用等).它可以幫我們完成許多事情.例如對(duì)對(duì)象數(shù)據(jù)的處理.構(gòu)造函數(shù)的數(shù)據(jù)驗(yàn)證,說(shuō)白了就是我們?cè)L問(wèn)對(duì)象前添加了一層攔截.我們可以對(duì)其進(jìn)行許多操作.而這些由你自己定義.
基本語(yǔ)法
let proxy = new Proxy(target,handler)
參數(shù)
target 要使用Proxy包裝的目標(biāo)對(duì)象,(可以是任何類型的對(duì)象,包括原生數(shù)組,函數(shù),甚至另一個(gè)代理)
handler一個(gè)通常以函數(shù)作為屬性的對(duì)象,各屬性中的函數(shù)分別定義了在執(zhí)行各種操作時(shí)代理 p 的行為。
下面是一個(gè)簡(jiǎn)單的實(shí)例
const obj = {name:"zs",age:434};
let proxy = new Proxy(obj,{
//obj 為目標(biāo)對(duì)象, attr 為對(duì)象的屬性
get(obj,attr){ //當(dāng)我們獲取代理對(duì)象的屬性值是會(huì)走這個(gè)方法
return obj[attr]
},
// obj為目標(biāo)對(duì)象, attr為屬性名, value為屬性值
set(obj,attr,value){//當(dāng)我們?cè)O(shè)置代理對(duì)象的屬性值是會(huì)走這個(gè)方法
if(property === "age"){
if(value >= 100 ) throw new Error("the data format is not correct")
obj[property] = value
}
//表示成功
return true
}
})
proxy.age = 120,
console.log(proxy.name )
上面的代碼中當(dāng)我們通過(guò)代理對(duì)象設(shè)置name屬性值時(shí), 會(huì)調(diào)用set方法,此時(shí)我們判斷
設(shè)置的屬性是不是age, 如果是,則判斷age的值是不是大于100,如果是則拋出一個(gè)錯(cuò)誤,當(dāng)我們?cè)O(shè)置的數(shù)字不符合格式時(shí), 我們?cè)O(shè)置的屬性值就不會(huì)生效.只有當(dāng)age設(shè)置的數(shù)字符合要求時(shí),才能設(shè)置上屬性值.
相當(dāng)于我們對(duì)象添加了一層攔截,只有當(dāng)我們的數(shù)據(jù)符合我們攔截是所設(shè)置的要求時(shí),我們才能得到自己想要的結(jié)果.
代理對(duì)象相當(dāng)于一層中介,我們不直接操作對(duì)象數(shù)據(jù),而是通過(guò)這個(gè)代理對(duì)象操作數(shù)據(jù).
1 2. 簡(jiǎn)單的數(shù)據(jù)雙向綁定
了解了基本原理后,我們就可以通過(guò)代理來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單數(shù)據(jù)雙向綁定
下面是代碼的部分
假設(shè)我們HTML有如下結(jié)構(gòu)代碼
<input type="text" v-model="content">
<input type="text" v-model="title">
<input type="text" v-model="title">
<h3 v-bind="title"></h3>
下面是js代碼部分
function view(option) {// 接受一個(gè)參數(shù)對(duì)象
this.initData = option.data;// 給實(shí)例對(duì)象添加初始化數(shù)據(jù)對(duì)象
let proxy = new Proxy(this.initData, {
set(obj, prototype, value) {// 當(dāng)我們?cè)O(shè)置proxy的屬性名時(shí)會(huì)走這個(gè)方法
//更新視圖
document
.querySelectorAll(`[v-model="${prototype}"]`)// 獲取相應(yīng)的表單元素
.forEach((item) => {
item.value = value
})
document.querySelectorAll(`[v-bind="${prototype}"]`)// 獲取相應(yīng)的普通的標(biāo)簽元素
.forEach((item) => {
item.innerHTML = value
})
return true
},
get(obj, prototype) { }
})
this.data = proxy;
// 初始化綁定監(jiān)聽(tīng)事件
this.init = function () {
let els = document.querySelectorAll("[v-model");
els.forEach((item) => {
item.addEventListener("input", function () {
// 此時(shí)通過(guò)getAttribute獲取的是我們綁定在DOM元素上的屬性值, 也就是相對(duì)應(yīng)數(shù)據(jù)的屬性名
proxy[this.getAttribute("v-model")] = this.value
})
})
}
// 初始化渲染
this.renderInit = function () {
for (let key in this.initData) {//對(duì)數(shù)據(jù)進(jìn)行遍歷, 獲取key獲取相應(yīng)的DOM元素并更新視圖.
let renerList = document.querySelectorAll(`[v-model="${key}"]`);
let renerInputList = document.querySelectorAll(`[v-bind="${key}"]`);
renerInputList.forEach((item) => item.innerHTML = this.initData[key])
renerList.forEach((item) => item.value = this.initData[key])
}
}
this.renderInit()// 渲染視圖
}
let data = {
title: "張三434",
content: "43",
}
let vm = new view({
data,//傳遞的數(shù)據(jù)對(duì)象, ES6簡(jiǎn)寫方式, 當(dāng)屬性名和屬性值相同時(shí),可以簡(jiǎn)寫這種形式.
})
vm.init()
//我們通過(guò)實(shí)例對(duì)象上的data屬性改變title屬性值, 此時(shí)也會(huì)被代理對(duì)象所攔截并響應(yīng).
vm.data.title = "$34"
上面代碼我們通過(guò)給構(gòu)造函數(shù)傳遞的參數(shù)option的data屬性, 給實(shí)例化對(duì)象身上添加了一個(gè)initData屬性, 然后對(duì)實(shí)例對(duì)象的initData對(duì)象添加了一層代理. 然后給實(shí)例對(duì)象上添加一個(gè)data屬性指向這個(gè)代理對(duì)象, 當(dāng)我們通過(guò)實(shí)例的data屬性改變相應(yīng)的數(shù)據(jù)時(shí),此時(shí)代理對(duì)象也會(huì)攔截到, 并更新相應(yīng)的視圖.
我們通過(guò)給實(shí)例對(duì)象添加了一個(gè)init初始化方法, 當(dāng)我們調(diào)用init方法時(shí),會(huì)獲取頁(yè)面上使用屬性使用了v-model的元素,并為他們添加了input事件, 當(dāng)這個(gè)事件觸發(fā)的時(shí)候,我們通過(guò)獲取當(dāng)前元素v-model綁定的數(shù)據(jù)名. 和value值.來(lái)相應(yīng)的設(shè)置proxy代理對(duì)象的鍵值對(duì), 此時(shí)我們代理對(duì)象的set方法會(huì)調(diào)用, 通過(guò)傳遞過(guò)來(lái)的屬性名, 獲取相應(yīng)的需要?jiǎng)討B(tài)顯示的元素,然后給相應(yīng)的DOM元素更新視圖.