受現代 JavaScript 的限制 ,Vue 無法檢測到對象屬性的添加或刪除。
由於 Vue 會在初始化實例時對屬性執行 getter/setter 轉化,所以屬性必須在 data 對象上存在才能讓 Vue 將它轉換為響應式的。
但是 Vue 提供了
Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value)
來實現為對象添加響應式屬性,那框架本身是如何實現的呢?
我們查看對應的 Vue 源碼:
vue/src/core/instance/index.js
export function set (target: Array<any> | Object, key: any, val: any): any { // target 為數組 if (Array.isArray(target) && isValidArrayIndex(key)) { // 修改數組的長度, 避免索引>數組長度導致splcie()執行有誤 target.length = Math.max(target.length, key) // 利用數組的splice變異方法觸發響應式 target.splice(key, 1, val) return val } // key 已經存在,直接修改屬性值 if (key in target && !(key in Object.prototype)) { target[key] = val return val } const ob = (target: any).__ob__ // target 本身就不是響應式數據, 直接賦值 if (!ob) { target[key] = val return val } // 對屬性進行響應式處理 defineReactive(ob.value, key, val) ob.dep.notify() return val }
我們閱讀以上源碼可知,vm.$set 的實現原理是:
- 如果目標是數組,直接使用數組的 splice 方法觸發相應式;
- 如果目標是對象,會先判讀屬性是否存在、對象是否是響應式,最終如果要對屬性進行響應式處理,則是通過調用 defineReactive 方法進行響應式處理( defineReactive 方法就是 Vue 在初始化對象時,給對象屬性采用 Object.defineProperty 動態添加 getter 和 setter 的功能所調用的方法)