十年網(wǎng)站開發(fā)經(jīng)驗 + 多家企業(yè)客戶 + 靠譜的建站團隊
量身定制 + 運營維護+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
vue中數(shù)據(jù)響應(yīng)式的原理是什么?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

響應(yīng)式的理解
響應(yīng)式顧名思義就是數(shù)據(jù)變化,會引起視圖的更新。這篇文章主要分析 vue2.0 中對象和數(shù)組響應(yīng)式原理的實現(xiàn),依賴收集和視圖更新我們留在下一篇文章分析。
在 vue 中,我們所說的響應(yīng)式數(shù)據(jù),一般指的是數(shù)組類型和對象類型的數(shù)據(jù)。vue 內(nèi)部通過 Object.defineProperty 方法對對象的屬性進行劫持,數(shù)組則是通過重寫數(shù)組的方法實現(xiàn)的。下面我們就簡單實現(xiàn)一下。
首先我們定義一個需要被攔截的數(shù)據(jù)
const vm = new Vue({
data () {
return {
count: 0,
person: { name: 'xxx' },
arr: [1, 2, 3]
}
}
})
let arrayMethods
function Vue (options) { // 這里只考慮對 data 數(shù)據(jù)的操作
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__ 屬性,說明已經(jīng)被攔截過了
return data
}
new Observer(data)
}這里的 arrayMethods、Observer 、 __ob__的實現(xiàn)和作用請繼續(xù)往下看
class Observer {
constructor (data) {
Object.defineProperty(data, '__ob__', { // 在 data 上定義 __ob__ 屬性,在數(shù)組劫持里需要用到
enumerable: false, // 不可枚舉
configurable: false, // 不可配置
value: this // 值是 Observer 實例
})
if (Array.isArray(data)) { // 對數(shù)組進行攔截
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) { // 攔截數(shù)組中的每一項
data.forEach(value => observer(value))
}
}對象的劫持需要注意的幾點:
遍歷對象,如果值還是對象類型,需要重新調(diào)用 observer 觀測方法
如果設(shè)置的新值是對象類型,也需要被攔截
// 處理對象的攔截
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) // 如果設(shè)置 newValue 值也是對象類型,需要被劫持
}
})
}數(shù)組的劫持需要注意的幾點:
數(shù)組是使用函數(shù)劫持(切片編程)的思想,對數(shù)據(jù)進行攔截的
數(shù)組里新增加的值,如果是對象類型,也需要被重新攔截
const oldArrayPrototype = Array.prototype
arrayMethods = Object.create(oldArrayPrototype)
const methods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'] // 能夠改變原數(shù)組的方法
methods.forEach(method => {
arrayMethods[methods] = function (...args) {
const result = oldArrayPrototype[methods].call(this, ...args)
const ob = this.__ob__ // this 就是調(diào)用改方法的數(shù)組
let inserted; // 數(shù)組新增的項的集合,需要再對其進行攔截
switch(methods) {
case 'push':
case 'unshift':
inserted = args
case 'splice':
inserted = args.slice(2) // 因為 splice 第二個參數(shù)后面的才是新增的
}
if (inserted) {
ob.observerArray(inserted)
}
return result
}
})在面試中,如果我們需要手寫 vue 的響應(yīng)式原理,上面的代碼足矣。但是我們通過學(xué)習(xí) vue 的源碼,如果在面試中能夠給出以下加以總結(jié)性的回答更能得到面試官的青睞。
vue 2.0 源碼的響應(yīng)式原理:
因為使用了遞歸的方式對對象進行攔截,所以數(shù)據(jù)層級越深,性能越差
數(shù)組不使用 Object.defineProperty 的方式進行攔截,是因為如果數(shù)組項太多,性能會很差
只有定義在 data 里的數(shù)據(jù)才會被攔截,后期我們通過 vm.newObj = 'xxx' 這種在實例上新增的方式新增的屬性是不會被攔截的
改變數(shù)組的索引和長度,不會被攔截,因此不會引起視圖的更新
如果在 data 上新增的屬性和更改數(shù)組的索引、長度,需要被攔截到,可以使用 $set 方法
可以使用 Object.freeze 方法來優(yōu)化數(shù)據(jù),提高性能,使用了此方法的數(shù)據(jù)不會被重寫 set 和 get 方法
vue 3.0 源碼響應(yīng)式原理:
3.0 版本中使用了 proxy 代替了 Object.defineProperty ,其有13中攔截方式,不需要對對象和數(shù)組分別進行處理,也無需遞歸進行攔截,這也是其提升性能較大的地方
vue 3.0 版本響應(yīng)式原理的簡單實現(xiàn)
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 是代理的目標(biāo)對象, handler 是配置對象
const proxy = new Proxy(obj, handler)看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進一步的了解或閱讀更多相關(guān)文章,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司,的支持。