你知道Vue中神奇的$set是如何實(shí)現(xiàn)的嗎?
前言
在日常開發(fā)中,$set的也是一個(gè)非常實(shí)用的API,因?yàn)閂ue2實(shí)現(xiàn)響應(yīng)式的核心是利用了ES5的Object.defineProperty,當(dāng)我們通過直接修改數(shù)組下標(biāo)更改數(shù)組或者給對(duì)象添加新的屬性,這個(gè)時(shí)候Object.defineproperty是監(jiān)聽不到數(shù)據(jù)的變化的,這時(shí)候大家就會(huì)用上$set,讓修改的操作也實(shí)現(xiàn)響應(yīng),我們知其然更要知其所以然,接下來看一下Vue中的$set是如何實(shí)現(xiàn)的
應(yīng)用場(chǎng)景
let dataArr = ["item1"];
let dataObject = {
name: "ccs"
};
dataArr[2] = "item2";
dataObject.age = 22;
// 響應(yīng)失敗,頁面沒有顯示更新新增的數(shù)據(jù)
this.$set(this.dataArr,2,'item2')
this.$set(this.dataObject,'age',22)
//響應(yīng)成功,頁面顯示更新新增的數(shù)據(jù)
set實(shí)現(xiàn)
接下來我們看一下$set在Vue中的定義
function set(target: Array<any> | Object, key: any, val: any): any {
if (
process.env.NODE_ENV !== "production" &&
(isUndef(target) || isPrimitive(target))
) {
warn(
`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`
);
}
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key);
target.splice(key, 1, val);
return val;
}
if (key in target && !(key in Object.prototype)) {
target[key] = val;
return val;
}
const ob = (target: any).__ob__;
if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== "production" &&
warn(
"Avoid adding reactive properties to a Vue instance or its root $data " +
"at runtime - declare it upfront in the data option."
);
return val;
}
if (!ob) {
target[key] = val;
return val;
}
defineReactive(ob.value, key, val);
ob.dep.notify();
return val;
}在源碼中首先判斷set的目標(biāo)是否是undefined和基本類型如果是undefined或基本類型就報(bào)錯(cuò),因?yàn)橛脩舨粦?yīng)該往undefined和基本類型中set東西,然后又判斷了目標(biāo)是否是數(shù)組與key是不是合法的index,合法的index是指值為大于等于0的整數(shù),如果兩個(gè)條件都成立就對(duì)目標(biāo)數(shù)組調(diào)用splice方法插入或者修改數(shù)組,這里的splice不是普通的splice,是王維詩里的splice,是被vue代理重寫過的splice
數(shù)組實(shí)現(xiàn)響應(yīng)
$set實(shí)現(xiàn)數(shù)組修改響應(yīng)的方式是代理重寫的數(shù)組的一部分方法,接下來我們看一下具體實(shí)現(xiàn)
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
function def(obj, key, val, enumerable) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
});
}
methodsToPatch.forEach(function (method) {
const original = arrayProto[method]
def(arrayMethods, method, function mutator (...args) {
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
ob.dep.notify()
return result
})
})vue中代理重寫的不只是splice,有push、pop、shift、unshift、splice、sort、reverse這七個(gè)方法, 首先執(zhí)行了const result = original.apply(this, args)執(zhí)行原本數(shù)組的方法并獲取它的值,接下來判斷如果是往數(shù)組中添加值就將新添加的值也實(shí)現(xiàn)響應(yīng)式,最后一步拿到這個(gè)數(shù)組的_ob_對(duì)象對(duì)_ob_里的dep進(jìn)行派發(fā)更新。
想深入了解vue的響應(yīng)式可以查閱往期文章面試官問你Vue2的響應(yīng)式原理,你怎么答?
對(duì)象實(shí)現(xiàn)響應(yīng)
$set中下半部分的邏輯就是用來處理對(duì)象響應(yīng)的,我們接著往下看
if (key in target && !(key in Object.prototype)) {
target[key] = val;
return val;
}
const ob = (target: any).__ob__;
if (!ob) {
target[key] = val;
return val;
}
defineReactive(ob.value, key, val);
ob.dep.notify();
return val;首先判斷了屬性如果在目標(biāo)對(duì)象中直接return結(jié)束邏輯,因?yàn)関ue只有添加目標(biāo)對(duì)象中原本沒有的屬性時(shí)才會(huì)失去響應(yīng)
例如 let obj={} obj.name='ccs',
vue在初始化的時(shí)候會(huì)將data里的所有屬性都變成響應(yīng)式,如果的值是對(duì)象或者數(shù)組則會(huì)new一個(gè)Observer實(shí)例儲(chǔ)存在__ob__,想深入了解vue的響應(yīng)式可以查閱往期文章面試官問你Vue2的響應(yīng)式原理,你怎么答
拿到這個(gè)對(duì)象的_ob_進(jìn)行判斷,如果不存在就說明是未經(jīng)過vue初始化的普通對(duì)象而不是響應(yīng)式對(duì)象 否則就手動(dòng)通過defineReactive為屬性添加get方法與set方法實(shí)現(xiàn)響應(yīng),然后手動(dòng)調(diào)用dep里的notify()發(fā)布更新。
總結(jié)
vue中$set方法對(duì)數(shù)組和對(duì)象的處理本質(zhì)上的一樣的,對(duì)新增的值添加響應(yīng)然后手動(dòng)觸發(fā)派發(fā)更新。
以上就是你知道Vue中神奇的$set是如何實(shí)現(xiàn)的嗎?的詳細(xì)內(nèi)容,更多關(guān)于Vue實(shí)現(xiàn)$set的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue中實(shí)現(xiàn)支持txt,docx,xlsx,mp4格式文件預(yù)覽功能(純前端)
對(duì)于Vue你可以實(shí)現(xiàn)文件的預(yù)覽功能,這篇文章主要給大家介紹了關(guān)于vue中實(shí)現(xiàn)支持txt,docx,xlsx,mp4格式文件預(yù)覽功能的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-11-11
vue3實(shí)現(xiàn)動(dòng)態(tài)側(cè)邊菜單欄的幾種方式簡(jiǎn)單總結(jié)
在做開發(fā)中都會(huì)遇到的需求,每個(gè)用戶的權(quán)限是不一樣的,那他可以訪問的頁面(路由)可以操作的菜單選項(xiàng)是不一樣的,如果由后端控制,我們前端需要去實(shí)現(xiàn)動(dòng)態(tài)路由,動(dòng)態(tài)渲染側(cè)邊菜單欄,這篇文章主要給大家介紹了關(guān)于vue3實(shí)現(xiàn)動(dòng)態(tài)側(cè)邊菜單欄的幾種方式,需要的朋友可以參考下2024-02-02
vue實(shí)現(xiàn)的上傳圖片到數(shù)據(jù)庫并顯示到頁面功能示例
這篇文章主要介紹了vue實(shí)現(xiàn)的上傳圖片到數(shù)據(jù)庫并顯示到頁面功能,結(jié)合實(shí)例形式分析了基于vue.js的數(shù)據(jù)庫操作及頁面圖片顯示相關(guān)操作技巧,需要的朋友可以參考下2018-03-03
使用element-ui實(shí)現(xiàn)行合并過程
這篇文章主要介紹了使用element-ui實(shí)現(xiàn)行合并過程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08
Vue項(xiàng)目npm操作npm run serve或npm run dev報(bào)錯(cuò)及二者
這篇文章主要介紹了Vue項(xiàng)目npm操作npm run serve或npm run dev報(bào)錯(cuò)及二者的區(qū)別說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10
VUE使用draggable實(shí)現(xiàn)組件拖拽
這篇文章主要為大家詳細(xì)介紹了VUE使用draggable實(shí)現(xiàn)組件拖拽,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
vue3.0語法糖內(nèi)的defineProps及defineEmits解析
這篇文章主要介紹了vue3.0語法糖內(nèi)的defineProps及defineEmits解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04

