你知道Vue中神奇的$set是如何實現(xiàn)的嗎?
前言
在日常開發(fā)中,$set
的也是一個非常實用的API,因為Vue2實現(xiàn)響應式的核心是利用了ES5的Object.defineProperty
,當我們通過直接修改數(shù)組下標更改數(shù)組或者給對象添加新的屬性,這個時候Object.defineproperty是監(jiān)聽不到數(shù)據(jù)的變化的,這時候大家就會用上$set
,讓修改的操作也實現(xiàn)響應,我們知其然更要知其所以然,接下來看一下Vue中的$set是如何實現(xiàn)的
應用場景
let dataArr = ["item1"]; let dataObject = { name: "ccs" }; dataArr[2] = "item2"; dataObject.age = 22; // 響應失敗,頁面沒有顯示更新新增的數(shù)據(jù) this.$set(this.dataArr,2,'item2') this.$set(this.dataObject,'age',22) //響應成功,頁面顯示更新新增的數(shù)據(jù)
set實現(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的目標是否是undefined
和基本類型如果是undefined
或基本類型就報錯,因為用戶不應該往undefined和基本類型中set東西,然后又判斷了目標是否是數(shù)組與key是不是合法的index,合法的index是指值為大于等于0的整數(shù),如果兩個條件都成立就對目標數(shù)組調(diào)用splice方法插入或者修改數(shù)組,這里的splice
不是普通的splice
,是王維詩里的splice,是被vue代理重寫過的splice
數(shù)組實現(xiàn)響應
$set實現(xiàn)數(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
這七個方法, 首先執(zhí)行了const result = original.apply(this, args)
執(zhí)行原本數(shù)組的方法并獲取它的值,接下來判斷如果是往數(shù)組中添加值就將新添加的值也實現(xiàn)響應式,最后一步拿到這個數(shù)組的_ob_對象
對_ob_
里的dep進行派發(fā)更新。
想深入了解vue的響應式可以查閱往期文章面試官問你Vue2的響應式原理,你怎么答?
對象實現(xiàn)響應
$set
中下半部分的邏輯就是用來處理對象響應的,我們接著往下看
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;
首先判斷了屬性如果在目標對象中直接return結(jié)束邏輯,因為vue只有添加目標對象中原本沒有的屬性時才會失去響應
例如 let obj={} obj.name='ccs',
vue在初始化的時候會將data里的所有屬性都變成響應式,如果的值是對象或者數(shù)組則會new一個Observer
實例儲存在__ob__,想深入了解vue的響應式可以查閱往期文章面試官問你Vue2的響應式原理,你怎么答
拿到這個對象的_ob_進行判斷,如果不存在就說明是未經(jīng)過vue初始化的普通對象而不是響應式對象 否則就手動通過defineReactive
為屬性添加get方法與set方法實現(xiàn)響應,然后手動調(diào)用dep
里的notify()
發(fā)布更新。
總結(jié)
vue中$set方法對數(shù)組和對象的處理本質(zhì)上的一樣的,對新增的值添加響應然后手動觸發(fā)派發(fā)更新。
以上就是你知道Vue中神奇的$set是如何實現(xiàn)的嗎?的詳細內(nèi)容,更多關(guān)于Vue實現(xiàn)$set的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue中實現(xiàn)支持txt,docx,xlsx,mp4格式文件預覽功能(純前端)
對于Vue你可以實現(xiàn)文件的預覽功能,這篇文章主要給大家介紹了關(guān)于vue中實現(xiàn)支持txt,docx,xlsx,mp4格式文件預覽功能的相關(guān)資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2023-11-11vue3實現(xiàn)動態(tài)側(cè)邊菜單欄的幾種方式簡單總結(jié)
在做開發(fā)中都會遇到的需求,每個用戶的權(quán)限是不一樣的,那他可以訪問的頁面(路由)可以操作的菜單選項是不一樣的,如果由后端控制,我們前端需要去實現(xiàn)動態(tài)路由,動態(tài)渲染側(cè)邊菜單欄,這篇文章主要給大家介紹了關(guān)于vue3實現(xiàn)動態(tài)側(cè)邊菜單欄的幾種方式,需要的朋友可以參考下2024-02-02vue實現(xiàn)的上傳圖片到數(shù)據(jù)庫并顯示到頁面功能示例
這篇文章主要介紹了vue實現(xiàn)的上傳圖片到數(shù)據(jù)庫并顯示到頁面功能,結(jié)合實例形式分析了基于vue.js的數(shù)據(jù)庫操作及頁面圖片顯示相關(guān)操作技巧,需要的朋友可以參考下2018-03-03Vue項目npm操作npm run serve或npm run dev報錯及二者
這篇文章主要介紹了Vue項目npm操作npm run serve或npm run dev報錯及二者的區(qū)別說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-10-10vue3.0語法糖內(nèi)的defineProps及defineEmits解析
這篇文章主要介紹了vue3.0語法糖內(nèi)的defineProps及defineEmits解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-04-04