vue的响应式的逻辑简单的说明:对象get时进行添加副作用函数,set时执行副作用函数
当我们对obj.text进行改变时需要页面刷新,我们需要重新执行这句代码,那么这就是副作用函数
javascript
// 副作用函数
function effect(){
document.body.textContent = obj.text
}
副作用函数执行,创建proxy触发get收集副作用函数,set时执行副作用函数,
javascript
let bucket = []
function reactive (target) {
return new Proxy(target, {
get (target, key, receiver) {
bucket.push(effect)
return target[key]
},
set (target, key, newVal, receiver) {
target[key] = newVal
bucket.forEach(effect =>{
effect()
})
return true
}
})
}
但是副作用函数需要变为多更为灵活,所以改写effect,定义全局变量activeEffect用于记录副作用函数
javascript
let activeEffect
let bucket = []
function effect(fn){
activeEffect = fn
// 思考为什么要执行一次
fn()
}
function reactive (target) {
return new Proxy(target, {
get (target, key, receiver) {
bucket.push(activeEffect)
return target[key]
},
set (target, key, newVal, receiver) {
target[key] = newVal
bucket.forEach(effect =>{
effect()
})
return true
}
})
}
但是使用会发现一个问题,就是bucket会重复收集副作用函数,因此我们需要使用Set()来避免重复记录副作用函数,而且如果同时存在两个key都有副作用函数,我们是无法分辨的
javascript
function effect1(){
document.getElementById('text').textContent = _obj.text
}
function effect2(){
document.getElementById('text2').textContent = _obj.text2
}
此时我们的副作用函数是要与key做对应
css
_obj
|
----text---effect1
|
----text2---effect2
改写如下:
javascript
let activeEffect
let bucket = new WeakMap()
function effect(fn){
activeEffect = fn
fn()
}
function reactive (target) {
return new Proxy(target, {
get (target, key, receiver) {
if (!bucket.get(target)) {
bucket.set(target, new Map())
}
if (!bucket.get(target).get(key)) {
bucket.get(target).set(key, new Set())
}
bucket.get(target).get(key).add(activeEffect)
return target[key]
},
set (target, key, newVal, receiver) {
target[key] = newVal
if (!bucket.get(target)) return
if (!bucket.get(target).get(key)) return
bucket.get(target).get(key).forEach(effect => {
effect()
})
return true
}
})
}
WeakMap可以搜一下它的解释,简单来说就是它可以对不用的key触发垃圾回收机制,Map则不会
我们提取get里面的处理逻辑到track函数中,set里面的处理逻辑到trigger中
javascript
function effect (fn) {
activeEffect = fn
fn()
}
function tarck (target, key) {
if (!bucket.get(target)) {
bucket.set(target, new Map())
}
if (!bucket.get(target).get(key)) {
bucket.get(target).set(key, new Set())
}
bucket.get(target).get(key).add(activeEffect)
}
function trigger (target, key) {
if (!bucket.get(target)) return
if (!bucket.get(target).get(key)) return
bucket.get(target).get(key).forEach(effect => {
effect()
})
}
function reactive (target) {
return new Proxy(target, {
get (target, key, receiver) {
tarck(target, key, receiver)
return target[key]
},
set (target, key, newVal, receiver) {
target[key] = newVal
trigger(target, key, newVal, receiver)
return true
}
})
}
使用:
javascript
let _obj = reactive({
text: '123',
text2: '123'
})
effect(() => {
document.getElementById('text').textContent = _obj.text
document.getElementById('text2').textContent = _obj.text2
})