您好,登錄后才能下訂單哦!
vue中數據響應式的原理是什么?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
響應式的理解
響應式顧名思義就是數據變化,會引起視圖的更新。這篇文章主要分析 vue2.0 中對象和數組響應式原理的實現,依賴收集和視圖更新我們留在下一篇文章分析。
在 vue 中,我們所說的響應式數據,一般指的是數組類型和對象類型的數據。vue 內部通過 Object.defineProperty 方法對對象的屬性進行劫持,數組則是通過重寫數組的方法實現的。下面我們就簡單實現一下。
首先我們定義一個需要被攔截的數據
const vm = new Vue({ data () { return { count: 0, person: { name: 'xxx' }, arr: [1, 2, 3] } } }) let arrayMethods function Vue (options) { // 這里只考慮對 data 數據的操作 let data = options.data if (data) { data = this._data = typeof data === 'function' ? data.call(this) : data } observer (data) } function observer(data) { if (typeof data !== 'object' || data === null) { return data } if (data.__ob__) { // 存在 __ob__ 屬性,說明已經被攔截過了 return data } new Observer(data) }
這里的 arrayMethods、Observer 、 __ob__的實現和作用請繼續往下看
class Observer { constructor (data) { Object.defineProperty(data, '__ob__', { // 在 data 上定義 __ob__ 屬性,在數組劫持里需要用到 enumerable: false, // 不可枚舉 configurable: false, // 不可配置 value: this // 值是 Observer 實例 }) if (Array.isArray(data)) { // 對數組進行攔截 data.__proto__ = arrayMethods // 原型繼承 this.observerArray(data) } else { // 對象進行攔截 this.walk(data) } } walk (data) { const keys = Object.keys(data) for(let i = 0; i < keys.length; i++) { const key = keys[i] defineReactive(data, key, data[key]) } } observerArray (data) { // 攔截數組中的每一項 data.forEach(value => observer(value)) } }
對象的劫持需要注意的幾點:
遍歷對象,如果值還是對象類型,需要重新調用 observer 觀測方法
如果設置的新值是對象類型,也需要被攔截
// 處理對象的攔截 function defineReactive(data, key, value) { observer(value) // 如果 value 值仍是對象類型,需要遞歸劫持 Object.defineProperty(data, key, { get() { return value }, set(newValue){ if (newValue === value) return value = newValue observer(newValue) // 如果設置 newValue 值也是對象類型,需要被劫持 } }) }
數組的劫持需要注意的幾點:
數組是使用函數劫持(切片編程)的思想,對數據進行攔截的
數組里新增加的值,如果是對象類型,也需要被重新攔截
const oldArrayPrototype = Array.prototype arrayMethods = Object.create(oldArrayPrototype) const methods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'] // 能夠改變原數組的方法 methods.forEach(method => { arrayMethods[methods] = function (...args) { const result = oldArrayPrototype[methods].call(this, ...args) const ob = this.__ob__ // this 就是調用改方法的數組 let inserted; // 數組新增的項的集合,需要再對其進行攔截 switch(methods) { case 'push': case 'unshift': inserted = args case 'splice': inserted = args.slice(2) // 因為 splice 第二個參數后面的才是新增的 } if (inserted) { ob.observerArray(inserted) } return result } })
在面試中,如果我們需要手寫 vue 的響應式原理,上面的代碼足矣。但是我們通過學習 vue 的源碼,如果在面試中能夠給出以下加以總結性的回答更能得到面試官的青睞。
vue 2.0 源碼的響應式原理:
因為使用了遞歸的方式對對象進行攔截,所以數據層級越深,性能越差
數組不使用 Object.defineProperty 的方式進行攔截,是因為如果數組項太多,性能會很差
只有定義在 data 里的數據才會被攔截,后期我們通過 vm.newObj = 'xxx' 這種在實例上新增的方式新增的屬性是不會被攔截的
改變數組的索引和長度,不會被攔截,因此不會引起視圖的更新
如果在 data 上新增的屬性和更改數組的索引、長度,需要被攔截到,可以使用 $set 方法
可以使用 Object.freeze 方法來優化數據,提高性能,使用了此方法的數據不會被重寫 set 和 get 方法
vue 3.0 源碼響應式原理:
3.0 版本中使用了 proxy 代替了 Object.defineProperty ,其有13中攔截方式,不需要對對象和數組分別進行處理,也無需遞歸進行攔截,這也是其提升性能最大的地方
vue 3.0 版本響應式原理的簡單實現
const handler = { get (target, key) { if (typeof target[key] === 'object' && target[key] !== null) { return new Proxy(target[key], handler) } return Reflect.get(target, key) }, set (target, key, value) { if(key === 'length') return true console.log('update') return Reflect.set(target, key, value) } } const obj = { arr: [1, 2, 3], count: { num: 1 } } // obj 是代理的目標對象, handler 是配置對象 const proxy = new Proxy(obj, handler)
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。