vue.js數(shù)據(jù)響應(yīng)式原理解析
Object.defineProperty()
得力于 Object.defineProperty() 的特性,vue 的數(shù)據(jù)變化有別于 react 和小程序,是非侵入式的。詳細介紹可以看 MDN 文檔,這里特別說明幾點:
- get / set 屬性是函數(shù),出于習(xí)慣會被稱為 getter 函數(shù) / setter 函數(shù)(Java,c++ 中都有這種慣例)
value 或 writable和get 或 set是不能同時出現(xiàn)的,否則報錯- 注意區(qū)別 Object.defineProperties()
定義 defineReactive 函數(shù)
Object.defineProperty() 在使用 getter 和 setter 的時候,要想實現(xiàn)屬性的修改,需要借助一個變量周轉(zhuǎn),如下面的 value,這就很麻煩。
const obj = {}
let value
Object.defineProperty(obj, 'a', {
enumerable: true,
configurable: true,
get() {
console.log('getter')
return value
},
set(newValue) {
value = newValue
console.log('setter', newValue)
}
})所以定義了 defineReactive 函數(shù),方便去給對象增加一個響應(yīng)式屬性。這里創(chuàng)建一個閉包的環(huán)境:閉包一定要有內(nèi)外兩個函數(shù),外面這個函數(shù) defineReactive 的 value 就形成了閉包。
const obj = {}
function defineReactive(data, key, value) {
// 如果只傳了兩個參數(shù),則讓 value 直接等于 data[key]
if (arguments.length === 2) value = data[key]
Object.defineProperty(data, key, {
enumerable: true, // 可被枚舉(for...in 或 Object.keys 方法)
configurable: true, // 可被配置,比如刪除
get() {
console.log('查看了' + key + '屬性')
return value
},
set(newValue) {
console.log('修改了' + key + '屬性')
value = newValue
}
})
}
defineReactive(obj, 'a', 10)
console.log(obj.a)
obj.a = 11
console.log(obj.a)得到的結(jié)果如下圖:

遞歸偵測對象的全部屬性
我們自己寫一個能夠偵測對象全部屬性的庫
新建 index.js 作為主入口文件,用于測試效果,我們 let 一個對象 obj,目標(biāo)是通過把 obj 作為參數(shù)傳給 observe 函數(shù),即可實現(xiàn)對 obj 對象所有屬性的查看與修改的監(jiān)測。
// index.js
import observe from './observe.js'
let obj = {
a: {
m: {
n: 1
}
},
b: 2
}
observe(obj)流程分析

observe 函數(shù)
observe 函數(shù)用于觀察一個對象(value)的屬性是否已被監(jiān)測的(是否有 __ob__ 屬性),如果不是則讓其屬性成為響應(yīng)式的(通過 new Observer(value))。
注意:之所以起了 __ob__ 這么奇怪的變量名,是為了保證不會與對象的原有屬性同名。
// observe.js
import Observer from './Observer.js'
export default (value) => {
if (typeof value !== 'object') return
if (value.__ob__ !== undefined) {
// 暫時留空
} else {
new Observer(value)
}
}Observer 類
Observer 是一個類,一旦 new 了一個實例,則做 2 件事:
- 給傳入的 value(其實是個對象) 添加
__ob__屬性,值為這次 new 的實例(也就是構(gòu)造函數(shù)中的 this),因為希望__ob__屬性是不可被枚舉的,所以用 def 函數(shù)處理。 - 遍歷 value 的屬性,通過 defineReactive 函數(shù)將其變?yōu)轫憫?yīng)式的
// Observer.js
import { def } from './utils.js'
import defineReactive from './defineReactive.js'
export default class Observer {
constructor(value) {
def(value, '__ob__', this, false)
this.walk(value)
}
// 處理對象,讓對象的屬性變?yōu)轫憫?yīng)式
walk(value) {
for (let key in value) {
defineReactive(value, key)
}
}
}def 函數(shù)定義如下:
export const def = (obj, key, value, enumerable) => {
Object.defineProperty(obj, key, {
value,
enumerable,
writable: true,
configurable: true
})
}完善 defineReactive 函數(shù)
相較于前面定義的時候,在兩個地方添加了 observe(value),從而實現(xiàn)了遞歸偵測對象的全部屬性。這里的參數(shù) value,就是已經(jīng)被變?yōu)轫憫?yīng)式的屬性的值,這個值如果是個對象,也需要被偵測,所以也要被 observe。
// defineReactive.js
import observe from './observe.js'
export default function defineReactive(data, key, value) {
if (arguments.length === 2) value = data[key]
// 注意這里不是傳 key 而是傳 value,因為 key 只是一個字符串,value 才是 key 指向的對象
observe(value)
// 讓 data 的 key 屬性變?yōu)轫憫?yīng)式屬性
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get() {
console.log('查看了' + key + '屬性')
return value
},
set(newValue) {
console.log('修改了' + key + '屬性')
value = newValue
// 修改的屬性也需要被觀察,如果是對象需要被偵測
observe(newValue)
}
})
}至此,在 index.js 傳入 observe 的 obj 的每個屬性都是響應(yīng)式的了
// index.js
...省略前面的代碼
obj.a.m = {
y: 8
}
console.log(obj.a.m.y)測試結(jié)果如下:

One More Thing
普通對象也是有 getter 和 setter 的:
- get propertyName(){} 用來得到當(dāng)前屬性值的回調(diào)函數(shù)
- set propertyName(){} 用來監(jiān)視當(dāng)前屬性值變化的回調(diào)函數(shù)
- 下面的代碼中,屬性 a 稱為“數(shù)據(jù)屬性”,它只有一個簡單的值;屬性b這種用 getter 和 setter 方法定義的屬性稱為“存取器屬性”。
var num= {
a: 2,
get b(){
return 2
}
}存取器屬性定義為一個或兩個與屬性同名的函數(shù),這個函數(shù)定義不使用 function 關(guān)鍵字,而是使用 get 或 set,也沒有使用冒號將屬性名和函數(shù)體分開。
到此這篇關(guān)于vue.js數(shù)據(jù)響應(yīng)式原理解析的文章就介紹到這了,更多相關(guān)vue.js響應(yīng)式原理 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue3響應(yīng)式實現(xiàn)readonly從零開始教程
這篇文章主要為大家介紹了vue3響應(yīng)式實現(xiàn)readonly從零開始教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03
VUE如何實現(xiàn)點擊文字添加顏色(動態(tài)修改class)
這篇文章主要介紹了VUE如何實現(xiàn)點擊文字添加顏色(動態(tài)修改class),具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11
Vue?中的?computed?和?watch?的區(qū)別詳解
這篇文章主要介紹了Vue中的computed和watch的區(qū)別詳解,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-09-09

