Vue2/3響應式原理實現
阿新 • • 發佈:2022-03-29
響應式原理
1.什麼是響應式
監聽物件中的屬性被設定或獲取的過程,當物件屬性變化時能夠監聽到,併發出通知
2.響應式函式的封裝
const reactiveFns = []
watchFn(fn) { }
3.Depend 類的封裝
class Depend { constructor() { this.reactiveFns = [] } addDepend(reactiveFn) { this.reactiveFns.push(reactiveFn) } notify() { this.reactiveFns.forEach(fn => { fn() }) } }
4. 監聽物件的變化
new Proxy(, set: depend.notify())
5. 依賴收集的資料結構
WeakMap
function getDepend() {}
6. 正確的收集依賴
Proxy的get方法中收集對應的函式
全域性activeReactiveFn變數
在get中找到depend物件, addDepend(全域性activeReactiveFn變數)
7. 對Depend進行優化
addDepend函式換成depend函式
直接獲取到自由變數
將之前儲存的陣列[]變成Set
8. 對物件的響應式操作
封裝reactive函式
- new Proxy(): Vue3
- Object.defineProperty(): Vue2
vue3響應式實現:
// 儲存當前需要收集的響應式函式 let activeReactiveFn = null /** * Depend優化: * 1> depend方法 * 2> 使用Set來儲存依賴函式, 而不是陣列[] */ class Depend { constructor() { this.reactiveFns = new Set() } // addDepend(reactiveFn) { // this.reactiveFns.add(reactiveFn) // } depend() { if (activeReactiveFn) { this.reactiveFns.add(activeReactiveFn) } } notify() { this.reactiveFns.forEach(fn => { fn() }) } } // 封裝一個響應式的函式 function watchFn(fn) { activeReactiveFn = fn fn() activeReactiveFn = null } // 封裝一個獲取depend函式 const targetMap = new WeakMap() function getDepend(target, key) { // 根據target物件獲取map的過程 let map = targetMap.get(target) if (!map) { map = new Map() targetMap.set(target, map) } // 根據key獲取depend物件 let depend = map.get(key) if (!depend) { depend = new Depend() map.set(key, depend) } return depend } function reactive(obj) { return new Proxy(obj, { get: function(target, key, receiver) { // 根據target.key獲取對應的depend const depend = getDepend(target, key) // 給depend物件中新增響應函式 // depend.addDepend(activeReactiveFn) depend.depend() return Reflect.get(target, key, receiver) }, set: function(target, key, newValue, receiver) { Reflect.set(target, key, newValue, receiver) // depend.notify() const depend = getDepend(target, key) depend.notify() } }) } // 監聽物件的屬性變數: Proxy(vue3)/Object.defineProperty(vue2) const objProxy = reactive({ name: "why", // depend物件 age: 18 // depend物件 }) const infoProxy = reactive({ address: "廣州市", height: 1.88 }) watchFn(() => { console.log(infoProxy.address) }) infoProxy.address = "北京市" const foo = reactive({ name: "foo" }) watchFn(() => { console.log(foo.name) }) foo.name = "bar"
vue2響應式實現:
// 儲存當前需要收集的響應式函式
let activeReactiveFn = null
/**
* Depend優化:
* 1> depend方法
* 2> 使用Set來儲存依賴函式, 而不是陣列[]
*/
class Depend {
constructor() {
this.reactiveFns = new Set()
}
// addDepend(reactiveFn) {
// this.reactiveFns.add(reactiveFn)
// }
depend() {
if (activeReactiveFn) {
this.reactiveFns.add(activeReactiveFn)
}
}
notify() {
this.reactiveFns.forEach(fn => {
fn()
})
}
}
// 封裝一個響應式的函式
function watchFn(fn) {
activeReactiveFn = fn
fn()
activeReactiveFn = null
}
// 封裝一個獲取depend函式
const targetMap = new WeakMap()
function getDepend(target, key) {
// 根據target物件獲取map的過程
let map = targetMap.get(target)
if (!map) {
map = new Map()
targetMap.set(target, map)
}
// 根據key獲取depend物件
let depend = map.get(key)
if (!depend) {
depend = new Depend()
map.set(key, depend)
}
return depend
}
function reactive(obj) {
// {name: "why", age: 18}
// ES6之前, 使用Object.defineProperty
Object.keys(obj).forEach(key => {
let value = obj[key]
Object.defineProperty(obj, key, {
get: function() {
const depend = getDepend(obj, key)
depend.depend()
return value
},
set: function(newValue) {
value = newValue
const depend = getDepend(obj, key)
depend.notify()
}
})
})
return obj
}
// 監聽物件的屬性變數: Proxy(vue3)/Object.defineProperty(vue2)
const objProxy = reactive({
name: "why", // depend物件
age: 18 // depend物件
})
const infoProxy = reactive({
address: "廣州市",
height: 1.88
})
watchFn(() => {
console.log(infoProxy.address)
})
infoProxy.address = "北京市"
const foo = reactive({
name: "foo"
})
watchFn(() => {
console.log(foo.name)
})
foo.name = "bar"
foo.name = "hhh"