vue3中的數(shù)據(jù)劫持的最新實現(xiàn)方案的proxy示例詳解
vuejs中實現(xiàn)數(shù)據(jù)的劫持,v2中使用的是Object.defineProperty()來實現(xiàn)的,在大版本v3中徹底重寫了這部分,使用了proxy這個數(shù)據(jù)代理的方式,來修復(fù)了v2中對數(shù)組和對象的劫持的遺留問題。
proxy是什么
Proxy 用于修改某些操作的默認行為,等同于在語言層面做出修改,所以屬于一種“元編程”(meta programming),即對編程語言進行編程。
Proxy 可以理解成對源對象進行一個封裝,在操作源對象之前,做了一系列額外的操作,最終返回我們需要的新數(shù)據(jù)對象。
基礎(chǔ)使用
let obj = new Proxy(
{},
{
get(target, prop, receiver) {
console.log("get", prop);
if (!target[prop]) target[prop] = 120;
return Reflect.get(target, prop, receiver);
},
set(target, prop, value, receiver) {
console.log("set", prop);
return Reflect.set(target, prop, value, receiver);
},
}
);
obj.count = 1;
obj.count;
obj.count;
obj.count;
console.log(obj.count);
obj.age;
console.log(obj.age);proxy實例有兩個參數(shù),一個是目標(biāo)對象,一個是操作方法的hash集合

取值函數(shù)get,賦值函數(shù)set。
對特定屬性的劫持
const proxyObj = new Proxy(
{ name: "Tom", age: 18 },
{
get: function (target, prop) {
if (prop === "age") return target[prop] - 1;
return 35;
},
}
);
proxyObj.time;
console.log("?? ~ proxyObj.time:", proxyObj.time);
proxyObj.age;
console.log("?? ~ proxyObj.time:", proxyObj.age);
把實例方法封裝在對象內(nèi)部
const object = {
name: "Tom",
age: 18,
sayHi() {
console.log("sayHi");
},
proxy() {
return new Proxy(this, {
get(target, prop) {
console.log("?? ~ get ~ prop:", prop);
if (prop in target) {
return Reflect.get(target, prop);
} else {
return "no prop";
}
return Reflect.get(target, prop);
},
});
},
};
const newProxy = object.proxy();
// newObjj.age;
console.log("?? ~ newObjj.age;:", newProxy.age);
console.log("?? ~ newObjj.name;:", newProxy.sex);
對數(shù)組進行負值索引的操作
function createArray(...elements) {
let handler = {
get(target, prop, receiver) {
let index = Number(prop);
if (index < 0) {
prop = String(target.length + index);
}
return Reflect.get(target, prop, receiver);
},
};
let target = [];
target.push(...elements);
return new Proxy(target, handler);
}
let arr = createArray("a", "b", "c");
arr[-1];
console.log("?? ~ arr[-1]:", arr[-1]);
console.log("?? ~ arr[-1]:", arr[-2]);
console.log("?? ~ arr[-1]:", arr[-3]);
實現(xiàn)數(shù)據(jù)的鏈?zhǔn)秸{(diào)用
var double = (n) => n * 2;
var pow = (n) => Math.pow(n, 2);
var reverse = (n) => String(n).split("").reverse().join("");
const pipe = function (value) {
var funcStack = [];
var oProxy = new Proxy(
{},
{
get: function (target, key) {
console.log("?? ~ pipe ~ key:", key);
if (key === "get") {
return funcStack.reduce(function (val, func) {
return func(val);
}, value);
}
// 把方法存儲到棧中
funcStack.push(window[key]);
console.log("?? ~ funcStack:", funcStack);
return oProxy;
},
}
);
return oProxy;
};
const data1 = pipe(3).double.pow.reverse.get;
console.log("?? ~ data:", data1);
注意:三個方法必須是var聲明的,let/const都會報錯



上面代碼設(shè)置 Proxy 以后,達到了將函數(shù)名鏈?zhǔn)绞褂玫男Ч?/p>
利用get攔截,實現(xiàn)一個生成各種 DOM 節(jié)點的通用函數(shù)dom
const dom = new Proxy(
{},
{
get(target, prop) {
return function (arrts, ...children) {
const el = document.createElement(prop);
for (let prop of Object.keys(arrts)) {
el.setAttribute(prop, arrts[prop]);
}
for (let child of children) {
console.log("?? ~ get ~ child:", child);
if (typeof child === "string") {
child = document.createTextNode(child);
}
el.appendChild(child);
}
return el;
};
},
}
);
const el = dom.div(
{},
"Hello, my name is ",
dom.a({ href: "http://example.com" }, "Mark"),
". I like:",
dom.ul(
{},
dom.li({}, "The web"),
dom.li({}, "Food"),
dom.li({}, "…actually that's it")
)
);
document.body.appendChild(el);
第三個參數(shù),它總是指向原始的讀操作所在的那個對象
const proxy = new Proxy(
{},
{
get: function (target, prop, receiver) {
console.log("?? ~ prop:", prop);
return receiver;
},
}
);
const isSame = proxy.getReceiver === proxy;
console.log("?? ~ isSame:", isSame);
const d = Object.create(proxy);
console.log("ddd", d.a === d);
如果一個屬性不可配置(configurable)且不可寫(writable),則 Proxy 不能修改該屬性
const target = Object.defineProperties(
{},
{
foo: { value: "bar", enumerable: false, configurable: false },
}
);
const handler = {
get(target, prop) {
return "abc";
},
};
const proxy2 = new Proxy(target, handler);
proxy2.foo;
上面通過 Proxy 對象訪問該屬性會報錯。
攔截方法的執(zhí)行
上面的都是object對象的屬性進行劫持,也可以作為方法調(diào)用時進行劫持。
var target = function () {
return "I am the target";
};
var handler = {
apply(target, thisArg, argumentsList) {
console.log("?? ~ apply ~ argumentsList:", argumentsList);
const res = target();
console.log("?? ~ apply ~ res:", res);
return "I am the proxy" + " " + argumentsList.join(",");
},
};
const p = new Proxy(target, handler);
const a = p("a", "b");
console.log("?? ~ a:", a);
變量p是 Proxy 的實例,當(dāng)它作為函數(shù)調(diào)用時(p()),就會被apply方法攔截,返回一個字符串
function sum(left, right) {
return left + right;
}
var twice = {
apply(target, context, args) {
console.log("?? ~ apply ~ context:", context);
console.log("?? ~ apply ~ args:", args);
return Reflect.apply(target, context, args) * 2;
},
};
const proxy = new Proxy(sum, twice);
const data = proxy(1, 2);
console.log("?? ~ data:", data);
const data2 = proxy.call(null, 2, 5);
console.log("?? ~ data2:", data2);
const data3 = proxy.apply(null, [5, 5]);
console.log("?? ~ data3:", data3);當(dāng)執(zhí)行proxy函數(shù)(直接調(diào)用或call和apply調(diào)用),就會被apply方法攔截。

get和set方法,實現(xiàn)內(nèi)部屬性的保護機制
const proxy = new Proxy(
{},
{
get(target, prop) {
invariant(prop, "get");
return Reflect.get(target, prop);
},
set(target, prop, value) {
invariant(prop, "set");
Reflect.set(target, prop, value);
return true;
},
}
);
function invariant(key, action) {
if (key[0] === "_") {
throw new Error(`Invalid attempt to ${action} private "${key}" property`);
}
}
// proxy._prop;
proxy._prop = "value";

攔截key in proxy的操作
var target = { _prop: "foo", prop: "foo" };
const proxy = new Proxy(target, {
has(target, key) {
if (key[0] === "_") {
console.log("false");
return false;
}
return key in target;
},
});
"_prop" in proxy; // false
deleteProperty刪除屬性的劫持
const handler = {
construct(target, args) {
console.log("called: " + args.join(","));
return new target(...args);
},
deleteProperty(target, prop) {
if (prop === "age") return false;
delete target[prop];
return true;
},
};
const P = new Proxy(function () {}, handler);
const p = new P(10);
P.value;
const p2 = new Proxy(
{
age: 20,
name: "John",
greet: () => console.log("hello"),
},
handler
);
delete p2.age;
delete p2.name;
到此這篇關(guān)于vue3中的數(shù)據(jù)劫持的最新實現(xiàn)方案的proxy的詳解的文章就介紹到這了,更多相關(guān)vue proxy內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vuex處理用戶Token過期及優(yōu)化設(shè)置封裝本地存儲操作模塊
這篇文章主要為大家介紹了Vuex處理用戶Token優(yōu)化設(shè)置封裝本地存儲操作模塊及Token?過期問題詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09
解決vue使用vant輪播組件swipe + flex時文字抖動問題
這篇文章主要介紹了解決vue使用vant輪播組件swipe + flex時文字抖動問題,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2021-01-01
vue3?組件與API直接使用的方法詳解(無需import)
這篇文章主要介紹了vue3?組件與API直接使用的方法(無需import),主要包括vue3自動導(dǎo)入和API的自動引入問題,本文結(jié)合示例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-09-09
VUE對Storage的過期時間設(shè)置,及增刪改查方式
這篇文章主要介紹了VUE對Storage的過期時間設(shè)置,及增刪改查方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-02-02
vue.js實現(xiàn)格式化時間并每秒更新顯示功能示例
這篇文章主要介紹了vue.js實現(xiàn)格式化時間并每秒更新顯示功能,結(jié)合實例形式分析了vue.js時間格式化顯示與基于定時器進行實時更新的相關(guān)操作技巧,需要的朋友可以參考下2018-07-07

