在日常開發中,
$set
的也是一個非常實用的API,因為Vue2實作響應式的核心是利用了ES5的Object.defineProperty
,當我們透過直接修改數組下標更改數組或為物件添加新的屬性,這時候Object.defineproperty是監聽不到資料的變化的,這時候大家就會用上$set
,讓修改的操作也實現響應,我們知其然更要知其所以然,接下來看一下Vue中的$set是如何實現的。 【相關推薦:vuejs影片教學、web前端開發】
let dataArr = ["item1"]; let dataObject = { name: "ccs" }; dataArr[2] = "item2"; dataObject.age = 22; 响应失败,页面没有显示更新新增的数据 this.$set(this.dataArr,2,'item2') this.$set(this.dataObject,'age',22) 响应成功,页面显示更新新增的数据
接下來我們來看看$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東西,
然後又判斷了目標是否是數組與key是不是合法的index,合法的index是指值為大於等於0的整數,
如果兩個條件都成立就對目標數組調用splice方法插入或者修改數組
,
這裡的splice
不是普通的splice
,是王維詩裡的splice,是被vue代理人重寫過的splice
$set實作陣列修改回應的方式是代理人重寫的陣列的一部分方法,接下來我們看一下具體實作
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
這七個方法,
首先執行了const result = original.apply(this, args)
#執行原本數組的方法並取得它的值,接下來判斷如果是往數組中新增值就將新加入的值也實作響應式
,
最後一步拿到這個陣列的_ob_物件
對_ob_
裡的dep進行派發更新。
想深入了解vue的回應式可以查閱往期文章
面試官問你Vue2的回應式原理,你怎麼答? - 掘金(juejin.cn)
$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結束邏輯,
因為vue只有在新增目標物件中原本沒有的屬性時才會失去回應,例如let obj={} obj.name='ccs'
,
vue在初始化的時候會將data裡的所有屬性都變成響應式,如果的值是物件或陣列則會new一個Observer
實例儲存在__ob__,想深入了解vue的回應式可以查閱往期文章
面試官問你Vue2的響應式原理,你怎麼答? - 掘金(juejin.cn)
拿到這個物件的_ob_進行判斷,如果不存在就表示是未經過vue初始化的普通物件而不是回應式物件否則就手動透過defineReactive
為屬性新增get方法與set方法實作回應,
然後手動呼叫dep
裡的notify()
發布更新。
vue中$set方法對數組和物件的處理本質上的一樣的,對新增的值添加回應然後手動觸發派發更新。
以上是聊聊Vue中$set是如何實現的?的詳細內容。更多資訊請關注PHP中文網其他相關文章!