本文主要介紹vue響應(yīng)式數(shù)據(jù)的簡易實(shí)現(xiàn)過程
由于我的語言組織表達(dá)能力不足贝椿,所以就只能在代碼片段末尾,附上了我的個(gè)人思路。
// 發(fā)布者類
class Dep {
constructor() {
// 訂閱者
this.subscribers = new Set()
}
// 收集訂閱者
depend() {
if (activeEffect) {
this.subscribers.add(activeEffect)
}
}
// 通知訂閱者更新數(shù)據(jù)
notify() {
this.subscribers.forEach(fun => {
fun()
})
}
}
// 用來收集訂閱者
let activeEffect = null
function watchEffect(effect) {
activeEffect = effect
effect()
activeEffect = null
}
/**
* WeakMap: 用來包裹傳入的對象(垃圾回收機(jī)制)
* 傳入的對象:是一個(gè)map對象(用來建立對應(yīng)的映射)
* map對象:每一項(xiàng)都是一個(gè)Dep(發(fā)布者)
*
* 也就是說:getDep函數(shù)的作用就是給每一項(xiàng)數(shù)據(jù)都建立一個(gè)對應(yīng)的發(fā)布者
*/
const targetMap = new WeakMap()
// 用來建立對應(yīng)的發(fā)布者
function getDep(target, key) {
// 根據(jù)傳入的對象,獲取對應(yīng)的map對象
let depsMap = targetMap.get(target)
if (!depsMap) {
depsMap = new Map()
// 給對應(yīng)的map對象賦值
targetMap.set(target, depsMap)
}
// 獲取key屬性對應(yīng)的dep對象(獲取key對應(yīng)的發(fā)布者)
let dep = depsMap.get(key)
if (!dep) {
dep = new Dep()
depsMap.set(key, dep)
}
return dep
}
// vue2
function reactive(raw) {
Object.keys(raw).forEach(key => {
let value = raw[key]
const dep = getDep(raw, key)
Object.defineProperty(raw, key, {
get() {
// 收集依賴
dep.depend()
return value
},
set(newValue) {
value = newValue
// 通知訂閱者們更新數(shù)據(jù)
dep.notify()
}
})
})
return raw
}
// vue3
// function reactive(raw) {
// return new Proxy(raw, {
// get(target, key) {
// const dep = getDep(raw, key)
// dep.depend()
// return target[key]
// },
// set(target, key, newValue) {
// const dep = getDep(raw, key)
// target[key] = newValue
// dep.notify()
// }
// })
// }
// 測試代碼
const info = reactive({ name: 'summer', age: 18 })
const info2 = reactive({ height: 1.88, weight: 188 })
watchEffect(function () {
console.log('effect1', info.name, info.age)
})
watchEffect(function () {
console.log('effect2', info2.height, info2.weight)
})
info.name = 'black'
// info2.height = 1.66
/**
* 思路:
* 1. 定義變量并賦值時(shí)酥诽,對變量進(jìn)行截取(reactive),利用Object.defineProperty重寫它的get和set方法
* get方法中dep.depend用于收集數(shù)據(jù)的依賴(訂閱者)
* set方法中dep.notify用于通知訂閱者更新數(shù)據(jù)
* 2. reactive方法中有g(shù)etDep()方法的執(zhí)行遏弱,getDep方法的目的是:給每個(gè)數(shù)據(jù)項(xiàng)建立一個(gè)對應(yīng)的發(fā)布者
* 當(dāng)執(zhí)行這個(gè)函數(shù)的時(shí)候會傳入兩個(gè)參數(shù):target目標(biāo)對象盆均,key目標(biāo)對象中的屬性塞弊,
* 第一次執(zhí)行這個(gè)函數(shù)時(shí)漱逸,target還是普通對象,這時(shí)候?qū)arget轉(zhuǎn)化為map對象游沿,并且target和map-target作為targetMap的一個(gè)鍵值對饰抒,
* 這時(shí)候再將第一次執(zhí)行的屬性(target對象里的屬性)的屬性值都設(shè)置為dep對象(發(fā)布者)
* 3. 類Dep,用于創(chuàng)建發(fā)布者(dep)和收集這個(gè)發(fā)布者對應(yīng)的訂閱者(subscribers)的诀黍,當(dāng)一個(gè)數(shù)據(jù)被重新賦值時(shí)袋坑,就通過notify通知訂閱者執(zhí)行,更新數(shù)據(jù)
* Dep中的depend用于收集這個(gè)發(fā)布者(數(shù)據(jù)項(xiàng))對應(yīng)的所有訂閱者
*/
如果看完了這篇文章眯勾,對vue整個(gè)響應(yīng)式流程感興趣的話婆誓,可以看我的另一篇文章http://www.reibang.com/p/280c45ddbc8f。
如有錯(cuò)誤也颤,歡迎指正洋幻!