成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Vue.js設計與實現之十-原始類型的響應式代理

開發 前端
在本文中主要介紹了如何將原始值轉為響應式數據,如何解決響應式丟失的問題,如何減少用戶心智負擔實現自動脫ref的能力等。

1、寫在前面

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

2、ref

Proxy的代理目標必須是對象類型,那么是否可以將原始值類型包裝成對象類型,這樣不就可以實現代理了嗎?

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

name.value = "onechuan";

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

  • 用戶創建一個原始值的響應式數據,就必須創建一個包裹的對象。
  • 而包裹對象又是由用戶自定義,那么就存在命名和使用不規范情況。

解決方法很簡單,你不是擔心用戶自定義的對象不規范不可控嗎,那么就在源碼內部定義不就行了。

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

簡單試用下:

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

但是,在使用過程中又有個問題:你又是如何保證refVal是原始值的包裹對象,還是一個非原始值的響應式數據呢?

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

其實,ref和reactive生成的響應式數據實現方式都是一樣的,對數據來源區分是不是ref是為了后續脫ref,脫出響應式能力恢復原始數據。

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

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

簡而言之:ref其實是對一個對象和reactive的二次封裝。

3、響應丟失的問題

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

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

在上面代碼中,副作用函數中訪問的只是普通對象newObj的屬性name的值,它并不具有響應式能力,在對其屬性值進行修改時,不會觸發副作用函數重新執行。

那么,應該如何解決響應式丟失的問題呢?

其實就是能解決在副作用函數中,通過獲取普通對象newObj的屬性值,也會觸發更新,與副作用函數建立聯系。

通過在普通對象newObj中設置與obj對象同名的屬性,將每個屬性值都設置成對象,通過對象的get取值方法實現obj對象的屬性值讀取,這樣就巧妙地將newObj的屬性值與副作用函數建立了聯系。

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

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

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";

前面只是對少數對象的屬性值轉成響應式數據可以這樣處理,但是當我們需要批量處理數據,應該如何處理呢?

很簡單,對對象屬性進行遍歷不就得了。

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

這樣,響應式丟失問題就被解決了,方法就是將響應式數據轉換成類似ref結構的數據,通過toRef或toRefs轉換后得到的數據就是真正的ref數據。

4、自動脫ref

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

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

現在我們就需要讓其自動脫ref,這樣在進行對象屬性的訪問時,讀取到屬性是個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));

在上面代碼中,通過定義一個proxyRefs函數接收一個對象參數,返回該對象的代理對象。而代理對象的作用是通過get操作,在讀取到對象的屬性是個ref值時,直接返回該ref.value值,否則直接返回屬性值,這樣就實現了自動脫ref。

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

前面有實現自動脫ref的能力,現在就有實現自動穿ref的能力。實現原理,同樣的是通過添加對應的set攔截函數。

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、寫在最后

在本文中主要介紹了如何將原始值轉為響應式數據,如何解決響應式丟失的問題,如何減少用戶心智負擔實現自動脫ref的能力等。ref本質就是一個包裹對象,通過reactive實現對原始值的響應式代理,但是包裹對象自愛本質上又和普通對象沒啥區別,對此需要通過設置一個標識符__v_isRef來實現ref數據的區分。

責任編輯:姜華 來源: 前端一碼平川
相關推薦

2022-04-16 13:59:34

Vue.jsJavascript

2022-04-05 16:44:59

系統Vue.js響應式

2022-04-04 16:53:56

Vue.js設計框架

2022-04-01 08:08:27

Vue.js框架命令式

2022-04-25 07:36:21

組件數據函數

2022-04-12 08:08:57

watch函數options封裝

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響應式代碼

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計算屬性

2022-04-14 09:35:03

Vue.js設計Reflect

2022-05-03 21:18:38

Vue.js組件KeepAlive

2022-04-03 15:44:55

Vue.js框架設計設計與實現

2021-04-14 12:47:50

Vue.jsMJML電子郵件

2022-04-19 23:01:54

Vue.jsDOM節點DOM樹

2022-04-20 09:07:04

Vue.js的事件處理

2021-09-18 10:07:23

開發技能代碼

2016-11-01 19:10:33

vue.js前端前端框架
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产视频一区二区在线观看 | 久久精品亚洲精品国产欧美 | 久久久www成人免费精品 | 香蕉视频黄色 | 国产在线精品一区二区三区 | 亚洲精品久久久久久下一站 | 国产一极毛片 | 精品欧美一区二区中文字幕视频 | 色橹橹欧美在线观看视频高清 | 黄色一级大片在线免费看产 | 男人亚洲天堂 | 91精品入口蜜桃 | 亚州中文| a级在线免费 | 欧美一级一 | 日韩av看片 | 伊人久久精品一区二区三区 | 久久久涩 | 二区国产 | 天天看天天操 | 国产在线色 | 成年人在线观看 | 少妇黄色 | 99免费在线视频 | 精品av天堂毛片久久久借种 | 国产精选一区 | 日韩成人在线播放 | 欧美美女爱爱 | 老外几下就让我高潮了 | 99热这里 | 久久精品久久久 | av一区二区三区在线观看 | 自拍偷拍亚洲视频 | 久久新| 精品在线一区 | 亚洲精品一区二三区不卡 | 久艹av | 成人在线免费视频 | 日韩免费视频一区二区 | 日韩国产欧美一区 | 欧美日韩一区在线 |