js
const bucket = new Set()
const ref = (data) => {
return new Proxy(data, {
get(target, key) {
bucket.add(effect) // 写死使用data数据的是 effect 函数
return target[key]
},
set(target, key, newVal) {
target[key] = newVal
bucket.forEach(fn => fn()) // 全部执行
return true
}
})
}
const data = { text: 'hello world' }
const refData = ref(data)
function effect() {
const res = refData.text
console.log(res) // 一般为副作用函数,操作dom,或其他共享状态
}
effect()
setTimeout(() => {
refData.text = 'hello vue3'
}, 1000);
硬编码使用响应式数据(被自动执行)的是 effect名函数
副作用函数也存起来 createEffect
js
let activeEffect = null // <-- ✨ 临时变量 存需要记录的副作用函数(使用响应式数据方)
const createEffect = (fn) => {
activeEffect = fn
fn()
}
const bucket = new Set()
const ref = (data) => {
return new Proxy(data, {
get(target, key) {
bucket.add(activeEffect) // 写死使用data数据的是 activeEffect 变量
return target[key]
},
set(target, key, newVal) {
target[key] = newVal
bucket.forEach(fn => fn()) // 全部执行
return true
}
})
}
const data = { text: 'hello world' }
const refData = ref(data)
function effect() {
const res = refData.text
console.log(res) // 一般为副作用函数,操作dom,或其他共享状态
}
createEffect(effect) // <-- ✨ 记录副作用函数(使用响应式数据方) 避免写死使用方函数名
setTimeout(() => {
refData.text = 'hello vue3'
}, 1000);
此时响应式数据对应的使用方自动执行逻辑,并没用做到具体的属性对应具体的使用方(副作用)函数
我们捋一下他们之间的对应关系
一个响应式数据对象 对应多个属性 一个属性 对应多个使用方(副作用)函数
js
const bucket = new Map([
[Obj, new Map([
[ key, new Set([fn1, fn2]) ],
// ... other key
])],
// ...other Obj
])
js
let activeEffect = null // <-- ✨ 临时变量 存需要记录的副作用函数(使用响应式数据方)
const createEffect = (fn) => {
activeEffect = fn
fn()
}
const bucket = new Map()
const ref = (data) => {
return new Proxy(data, {
get(target, key) {
const depsMap = bucket.get(target)
if(!depsMap) {
bucket.set(target, new Map())
}
const depsFns = depsMap.get(key) // <-- ❌ TypeError: Cannot read property 'get' of undefined
if(!depsFns) {
depsMap.set(key, new Set())
}
depsFns.add(activeEffect) // 写死使用data数据的是 activeEffect 变量
return target[key]
},
set(target, key, newVal) {
target[key] = newVal
const depsMap = bucket.get(target)
const depsFns = depsMap.get(key)
depsFns.forEach(fn => fn()) // 全部执行
return true
}
})
}
const data = { text: 'hello world' }
const refData = ref(data)
function effect() {
const res = refData.text
console.log(res) // 一般为副作用函数,操作dom,或其他共享状态
}
createEffect(effect) // <-- ✨ 记录副作用函数(使用响应式数据方) 避免写死使用方函数名
setTimeout(() => {
refData.text = 'hello vue3'
}, 1000);
因为 const depsMap
被赋值为 undefined 了,即使后面马上设置了初始值,但那是 bucket 的值,因此要在get一次
这里这样简写👇
js
let depsMap = bucket.get(target)
if(!depsMap) {
bucket.set(target, (depsMap = new Map()))
}
let depsFns = depsMap.get(key)
if(!depsFns) {
depsMap.set(key, (depsFns = new Set()))
}
我们这样输出一个js赋值表达式: let a = 1; console.log((a = 2)
会得到赋值后 a 的值 --> 2
js
let activeEffect = null // <-- ✨ 临时变量 存需要记录的副作用函数(使用响应式数据方)
const createEffect = (fn) => {
activeEffect = fn
fn()
}
const bucket = new Map()
const ref = (data) => {
return new Proxy(data, {
get(target, key) {
let depsMap = bucket.get(target)
if(!depsMap) {
bucket.set(target, (depsMap = new Map())) // <-- ✨ 初始化数据结构 同时赋值
}
let depsFns = depsMap.get(key)
if(!depsFns) {
depsMap.set(key, (depsFns = new Set()))
}
depsFns.add(activeEffect) // 写死使用data数据的是 activeEffect 变量
return target[key]
},
set(target, key, newVal) {
target[key] = newVal
const depsMap = bucket.get(target)
const depsFns = depsMap.get(key)
depsFns.forEach(fn => fn()) // 全部执行
return true
}
})
}
const data = { text: 'hello world' }
const refData = ref(data)
function effect() {
const res = refData.text
console.log(res) // 一般为副作用函数,操作dom,或其他共享状态
}
createEffect(effect) // <-- ✨ 记录副作用函数(使用响应式数据方) 避免写死使用方函数名
setTimeout(() => {
refData.text = 'hello vue3'
}, 1000);
js
const map = new Map()
const weakmap = new WeakMap()
(() => {
const foo = {foo:1}
const bar = {bar:1}
map.set(foo, 1)
weakmap.set(bar, 1)
})()
不在代码中读取 foo
和 bar
, 各map set时是最后一次读取,垃圾回收将开始清除
此时输出 map
和 weakmap
行为不相同
👇 使用 WeakMap
并 抽象出 追踪(track)
、触发(trigger)
函数
js
let activeEffect = null // <-- ✨ 临时变量 存需要记录的副作用函数(使用响应式数据方)
const createEffect = (fn) => {
activeEffect = fn
fn()
}
// ✨ 追踪 创建响应式数据和副作用函数的对应关系数据结构
const track = (target, key) => {
let depsMap = bucket.get(target)
if(!depsMap) {
bucket.set(target, (depsMap = new Map())) // <-- ✨ 初始化数据结构 同时赋值
}
let depsFns = depsMap.get(key)
if(!depsFns) {
depsMap.set(key, (depsFns = new Set()))
}
depsFns.add(activeEffect) // 写死使用data数据的是 activeEffect 变量
}
// ✨ 触发 找到变动数据对应的副作用函数并执行
const trigger = (target, key) => {
const depsMap = bucket.get(target)
const depsFns = depsMap.get(key)
depsFns.forEach(fn => fn()) // 全部执行
}
const bucket = new WeakMap() // ✨ WeakMap 外部业务代码不再使用响应式数据时自动回收
const ref = (data) => {
return new Proxy(data, {
get(target, key) {
track(target, key) // ✨ 追踪 创建响应式数据和副作用函数的对应关系数据结构
return target[key]
},
set(target, key, newVal) {
target[key] = newVal
trigger(target, key) // ✨ 触发 找到变动数据对应的副作用函数并执行
return true
}
})
}
const data = { text: 'hello world' }
const refData = ref(data)
function effect() {
const res = refData.text
console.log(res) // 一般为副作用函数,操作dom,或其他共享状态
}
createEffect(effect) // <-- ✨ 记录副作用函数(使用响应式数据方) 避免写死使用方函数名
setTimeout(() => {
refData.text = 'hello vue3'
}, 1000);