自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Vue.js設(shè)計(jì)與實(shí)現(xiàn)之十-原始類型的響應(yīng)式代理

開發(fā) 前端
在本文中主要介紹了如何將原始值轉(zhuǎn)為響應(yīng)式數(shù)據(jù),如何解決響應(yīng)式丟失的問題,如何減少用戶心智負(fù)擔(dān)實(shí)現(xiàn)自動(dòng)脫ref的能力等。

1、寫在前面

在javascript中原始值包括:Boolean、String、Number、Null、Undefined、Symbol和BigInt等類型,原始值是按值傳遞而非按引用傳遞。前面,知道Proxy可以用于實(shí)現(xiàn)對(duì)象類型的響應(yīng)式代理,但是卻不能實(shí)現(xiàn)原始值的代理,要實(shí)現(xiàn)原始值變成響應(yīng)式數(shù)據(jù),就需要做些處理。

2、ref

Proxy的代理目標(biāo)必須是對(duì)象類型,那么是否可以將原始值類型包裝成對(duì)象類型,這樣不就可以實(shí)現(xiàn)代理了嗎?

// let name = "pingping"
const data = {
value: "pingping"
}
const state = reactive(data);

name.value = "onechuan";

想法是很好,但是你想過沒有這樣做帶來的問題:

  • 用戶創(chuàng)建一個(gè)原始值的響應(yīng)式數(shù)據(jù),就必須創(chuàng)建一個(gè)包裹的對(duì)象。
  • 而包裹對(duì)象又是由用戶自定義,那么就存在命名和使用不規(guī)范情況。

解決方法很簡單,你不是擔(dān)心用戶自定義的對(duì)象不規(guī)范不可控嗎,那么就在源碼內(nèi)部定義不就行了。

function ref(val){
const wrapper = {
value: val
}
return reactive(wrapper);
}

簡單試用下:

const refVal = ref("pingping");
effect(()=>{
console.log(refVal.value);
});
refVal.value = "onechuan";

但是,在使用過程中又有個(gè)問題:你又是如何保證refVal是原始值的包裹對(duì)象,還是一個(gè)非原始值的響應(yīng)式數(shù)據(jù)呢?

const refVal = ref("pingping");
const refVal2 = reactive({value:"pingping"});

其實(shí),ref和reactive生成的響應(yīng)式數(shù)據(jù)實(shí)現(xiàn)方式都是一樣的,對(duì)數(shù)據(jù)來源區(qū)分是不是ref是為了后續(xù)脫ref,脫出響應(yīng)式能力恢復(fù)原始數(shù)據(jù)。

function ref(val){
const wrapper = {
value: val
}
Object.defineProperty(wrapper,"__v_isRef",{
value: true
})
return reactive(wrapper);
}

在上面代碼中,使用Object.defineProperty給包裹對(duì)象wrapper定義一個(gè)不可枚舉和不可寫的屬性"__v_isRef",使其值為true用于區(qū)分當(dāng)前對(duì)象是ref而非普通對(duì)象。

簡而言之:ref其實(shí)是對(duì)一個(gè)對(duì)象和reactive的二次封裝。

3、響應(yīng)丟失的問題

我們知道,ref可以用于實(shí)現(xiàn)原始值的響應(yīng)式代理,但其實(shí)還可以用于解決響應(yīng)式丟失的問題。所謂響應(yīng)式丟失,就是在使用reactive生成的響應(yīng)式對(duì)象數(shù)據(jù),使用展開運(yùn)算符(...)會(huì)丟失響應(yīng)式,就成了一個(gè)普通對(duì)象數(shù)據(jù)。此時(shí),修改修改對(duì)象的屬性值,不會(huì)觸發(fā)更新和模板渲染。

const obj = reactive({name:"pingping"});
const newObj = {...obj};
effect(()=>{
console.log(newObj.name);
});
obj.nmae = "onechuan";

在上面代碼中,副作用函數(shù)中訪問的只是普通對(duì)象newObj的屬性name的值,它并不具有響應(yīng)式能力,在對(duì)其屬性值進(jìn)行修改時(shí),不會(huì)觸發(fā)副作用函數(shù)重新執(zhí)行。

那么,應(yīng)該如何解決響應(yīng)式丟失的問題呢?

其實(shí)就是能解決在副作用函數(shù)中,通過獲取普通對(duì)象newObj的屬性值,也會(huì)觸發(fā)更新,與副作用函數(shù)建立聯(lián)系。

通過在普通對(duì)象newObj中設(shè)置與obj對(duì)象同名的屬性,將每個(gè)屬性值都設(shè)置成對(duì)象,通過對(duì)象的get取值方法實(shí)現(xiàn)obj對(duì)象的屬性值讀取,這樣就巧妙地將newObj的屬性值與副作用函數(shù)建立了聯(lián)系。

const obj = reactive({name:"pingping"});
const newObj = {...obj};
effect(()=>{
console.log(newObj.name);
});
obj.nmae = "onechuan";

但是,如果obj對(duì)象中有很多屬性,那是不是就需要在newObj建立許多同名的對(duì)象?那么,就可以進(jìn)行抽取封裝函數(shù):

function toRef(obj, key){
const wrapper = {
get value(){
return obj[key];
},
set value(val){
obj[key] = val
}
}
Object.defineProperty(wrapper,"__v_isRef",{
value: true
})
return wrapper;
}

在使用過程中,簡簡單單:

const obj = reactive({name:"pingping"});
const name = toRef(obj, "name");
name.value = "onechuan";

前面只是對(duì)少數(shù)對(duì)象的屬性值轉(zhuǎn)成響應(yīng)式數(shù)據(jù)可以這樣處理,但是當(dāng)我們需要批量處理數(shù)據(jù),應(yīng)該如何處理呢?

很簡單,對(duì)對(duì)象屬性進(jìn)行遍歷不就得了。

function toRefs(obj){
const res = {};
for(const key in obj){
res[key] = toRef(obj,key);
}
return res;
}

這樣,響應(yīng)式丟失問題就被解決了,方法就是將響應(yīng)式數(shù)據(jù)轉(zhuǎn)換成類似ref結(jié)構(gòu)的數(shù)據(jù),通過toRef或toRefs轉(zhuǎn)換后得到的數(shù)據(jù)就是真正的ref數(shù)據(jù)。

4、自動(dòng)脫ref

使用toRefs用于解決響應(yīng)丟失問題,就是對(duì)對(duì)象的屬性進(jìn)行遍歷轉(zhuǎn)為ref,這樣就會(huì)帶來新問題,就是去訪問數(shù)據(jù)的第一層屬性,必須通過.value才能訪問。這樣無疑會(huì)增加使用者的心智負(fù)擔(dān),用戶肯定愿意直接對(duì)象.屬性,而非通過對(duì)象.屬性.value來使用屬性值。

const obj = reactive({
name:"pingping",
age:18
});
const newObj = {
...toRefs(obj)
};
newObj.name.value//pingping
newObj.age.value//18

現(xiàn)在我們就需要讓其自動(dòng)脫ref,這樣在進(jìn)行對(duì)象屬性的訪問時(shí),讀取到屬性是個(gè)ref則放回ref.value,否則直接返回屬性值。

function proxyRefs(target){
return new Proxy(target,{
get(target, key, receiver){
const value = Reflect.get(target, key, receiver);
return value.__v_isRef ? value.value : value;
}
})
}
const newObj = proxyRefs(...toRefs(obj));

在上面代碼中,通過定義一個(gè)proxyRefs函數(shù)接收一個(gè)對(duì)象參數(shù),返回該對(duì)象的代理對(duì)象。而代理對(duì)象的作用是通過get操作,在讀取到對(duì)象的屬性是個(gè)ref值時(shí),直接返回該ref.value值,否則直接返回屬性值,這樣就實(shí)現(xiàn)了自動(dòng)脫ref。

其實(shí),在模板中使用ref的屬性值時(shí),就是通過將組件setup返回的數(shù)據(jù)傳遞到proxyRefs函數(shù)中進(jìn)行處理。這樣就可以實(shí)現(xiàn),在模板中直接訪問屬性值,而非屬性.value值。

前面有實(shí)現(xiàn)自動(dòng)脫ref的能力,現(xiàn)在就有實(shí)現(xiàn)自動(dòng)穿ref的能力。實(shí)現(xiàn)原理,同樣的是通過添加對(duì)應(yīng)的set攔截函數(shù)。

function proxyRefs(target){
return new Proxy(target,{
get(target, key, receiver){
const value = Reflect.get(target, key, receiver);
return value.__v_isRef ? value.value : value;
},
set(target, key, newValue, receiver){
const value = target[key];
if(value.__v_isRef){
value.value = newValue;
return true
}
return Reflect.set(target, key, newValue, receiver);
}
})
}

5、寫在最后

在本文中主要介紹了如何將原始值轉(zhuǎn)為響應(yīng)式數(shù)據(jù),如何解決響應(yīng)式丟失的問題,如何減少用戶心智負(fù)擔(dān)實(shí)現(xiàn)自動(dòng)脫ref的能力等。ref本質(zhì)就是一個(gè)包裹對(duì)象,通過reactive實(shí)現(xiàn)對(duì)原始值的響應(yīng)式代理,但是包裹對(duì)象自愛本質(zhì)上又和普通對(duì)象沒啥區(qū)別,對(duì)此需要通過設(shè)置一個(gè)標(biāo)識(shí)符__v_isRef來實(shí)現(xiàn)ref數(shù)據(jù)的區(qū)分。

責(zé)任編輯:姜華 來源: 前端一碼平川
相關(guān)推薦

2022-04-16 13:59:34

Vue.jsJavascript

2022-04-05 16:44:59

系統(tǒng)Vue.js響應(yīng)式

2022-04-04 16:53:56

Vue.js設(shè)計(jì)框架

2022-04-01 08:08:27

Vue.js框架命令式

2022-04-12 08:08:57

watch函數(shù)options封裝

2022-04-25 07:36:21

組件數(shù)據(jù)函數(shù)

2022-04-09 17:53:56

Vue.js分支切換嵌套的effect

2017-08-30 17:10:43

前端JavascriptVue.js

2021-01-22 11:47:27

Vue.js響應(yīng)式代碼

2022-04-26 05:55:06

Vue.js異步組件

2022-04-18 08:09:44

渲染器DOM掛載Vue.js

2022-04-11 08:03:30

Vue.jscomputed計(jì)算屬性

2022-04-14 09:35:03

Vue.js設(shè)計(jì)Reflect

2022-05-03 21:18:38

Vue.js組件KeepAlive

2022-04-03 15:44:55

Vue.js框架設(shè)計(jì)設(shè)計(jì)與實(shí)現(xiàn)

2021-04-14 12:47:50

Vue.jsMJML電子郵件

2022-04-19 23:01:54

Vue.jsDOM節(jié)點(diǎn)DOM樹

2022-04-20 09:07:04

Vue.js的事件處理

2021-09-18 10:07:23

開發(fā)技能代碼

2016-11-01 19:10:33

vue.js前端前端框架
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)