Proxy是怎么做數據劫持的


概述

Vue3的一個重大升級就是使用 proxy 來做數據劫持,我們來體驗一下用 proxy 是怎么做數據劫持的,供以后工作時參考,相信對其它人也有用。

Vue2.x的缺點

Vue2.x是使用Object.defineProperty來做數據劫持的,但是它有以下三個缺點:

1.不能劫持數組的變化,需要做特殊處理(通過劫持數組的push、splice等方法實現的)
2.必須深度遍歷對象的每個屬性
3.無法監聽屬性的新增和刪除操作(通過Vue.set 和 Vue.delete實現的)

使用 proxy

1.劫持數組的變化

const test = [];
const testProxy = new Proxy(test, {
    get(target, key) {
        if (key !== 'length') {
            console.log('get==================', target, key);
        }

        return target[key];
    },
    set(target, key, value) {
        if (key !== 'length') {
            console.log('set==================', target, key);
        }

        return Reflect.set(target, key, value);
    }
});

testProxy.push(1);
testProxy[0] = 2;
testProxy[1] = 'abc';

這里需要注意的是,這里使用push的時候,length屬性會發生變化,所以上面過濾掉了length屬性。(當然我這里做的比較簡單,而 Vue3 源碼里面是通過判斷 push、indexOf、shift 等方法來分情況做的。)

2.必須深度遍歷對象的每個屬性

const toProxy = new WeakMap();
const toRaw = new WeakMap();
const isObject = val => typeof val === 'object' && val !== null;
const proxyConfig = {
    get(target, key) {
        const res = Reflect.get(target, key);
        if (isObject(res)) {
            return reactive(res);
        }

        console.log('get==================', target, key);
        return res;
    },
    set(target, key, value) {
        console.log('set==================', target, key);
        return Reflect.set(target, key, value);
    }
}

function reactive(target) {
    const res = toProxy.get(target);

    if (res) {
        return res;
    }

    if (toRaw.get(target)) {
        return target;
    }

    const observed = new Proxy(target, proxyConfig);
    toProxy.set(target, observed);
    toRaw.set(observed, target);
    return observed;
}

const test = {
    a: 2,
    b: {
        c: 'abc',
    }
};
const testProxy = reactive(test);
console.log(testProxy.b.c);
testProxy.b.d = 3;

這里需要注意的是,當屬性是深度嵌套的時候,只會觸發 getter,並不會觸發setter,所以需要對深度嵌套的屬性進一步使用 proxy!

但是使用 proxy 得到的新屬性又不能掛載到對象上面去,所以需要使用一個 weakmap 儲存起來,同時也能加快查找速度。

3.監聽屬性的新增和刪除操作

const test = {a:2};
const testProxy = new Proxy(test, {
    deleteProperty(target, key) {
        console.log('delete==================', target, key);
        delete target[key];
    },
});

delete testProxy.a;

總結

1.proxy做數據劫持還是有一些痛點的,比如對於數組的push、unshift等方法需要做兼容,對於對象的深層屬性仍然需要通過遍歷來重新proxy。

2.proxy的功能不止如此,它還能攔截更多的屬性,比如 has、defineProperty、apply 等。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM