利用Proxy實(shí)現(xiàn)簡(jiǎn)單的數(shù)據(jù)雙向綁定

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元素更新視圖.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容