用reactive实现computed和watch
// 存储副作用函数的桶
const bucket = new WeakMap()
// 充当for in操作时候 对象的key
const ITERATE_KEY = Symbol()
// 实现reactive,创建对象的代理
function reactive(obj) {
return new Proxy(obj, {
// 拦截读取操作
get(target, key, receiver) {
if (key === 'raw') {
return target
}
// 将副作用函数 activeEffect 添加到存储副作用函数的桶中
track(target, key)
// 返回属性值
return Reflect.get(target, key, receiver)
},
// 拦截设置操作
set(target, key, newVal, receiver) {
const oldVal = target[key]
// 如果属性不存在,则说明是在添加新的属性,否则是设置已存在的属性
const type = Object.prototype.hasOwnProperty.call(target, key) ? 'SET' : 'ADD'
// 设置属性值
const res = Reflect.set(target, key, newVal, receiver)
console.log(target === receiver.raw)
if (target === receiver.raw) {
if (oldVal !== newVal && (oldVal === oldVal || newVal === newVal)) {
trigger(target, key, type)
}
}
return res
},
// in操作符监听
has(target, key) {
track(target, key)
return Reflect.has(target, key)
},
// for in操作符监听
ownKeys(target) {
track(target, ITERATE_KEY)
return Reflect.ownKeys(target)
},
// delete操作符监听
deleteProperty(target, key) {
const hadKey = Object.prototype.hasOwnProperty.call(target, key)
const res = Reflect.deleteProperty(target, key)
if (res && hadKey) {
trigger(target, key, 'DELETE')
}
return res
}
})
}
// 收集代理副作用函数
function track(target, key) {
if (!activeEffect) return
let depsMap = bucket.get(target)
if (!depsMap) {
bucket.set(target, (depsMap = new Map()))
}
let deps = depsMap.get(key)
if (!deps) {
depsMap.set(key, (deps = new Set()))
}
deps.add(activeEffect)
activeEffect.deps.push(deps)
}
// 触发执行副作用函数
function trigger(target, key, type) {
const depsMap = bucket.get(target)
if (!depsMap) return
const effects = depsMap.get(key)
const effectsToRun = new Set()
effects && effects.forEach(effectFn => {
if (effectFn !== activeEffect) {
effectsToRun.add(effectFn)
}
})
// console.log(type, key)
if (type === 'ADD' || type === 'DELETE') {
const iterateEffects = depsMap.get(ITERATE_KEY)
iterateEffects && iterateEffects.forEach(effectFn => {
if (effectFn !== activeEffect) {
effectsToRun.add(effectFn)
}
})
}
effectsToRun.forEach(effectFn => {
if (effectFn.options.scheduler) {
effectFn.options.scheduler(effectFn)
} else {
effectFn()
}
})
}
// 用一个全局变量存储当前激活的 effect 函数
let activeEffect
// effect 栈
const effectStack = []
// 将组副作用函数收集触发track函数
function effect(fn, options = {}) {
const effectFn = () => {
cleanup(effectFn)
// 当调用 effect 注册副作用函数时,将副作用函数复制给 activeEffect
activeEffect = effectFn
// 在调用副作用函数之前将当前副作用函数压栈
effectStack.push(effectFn)
const res = fn()
// 在当前副作用函数执行完毕后,将当前副作用函数弹出栈,并还原 activeEffect 为之前的值
effectStack.pop()
activeEffect = effectStack[effectStack.length - 1]
return res
}
// 将 options 挂在到 effectFn 上
effectFn.options = options
// activeEffect.deps 用来存储所有与该副作用函数相关的依赖集合
effectFn.deps = []
// 执行副作用函数
if (!options.lazy) {
effectFn()
}
return effectFn
}
// 解决副作用汉同时读取和设置同一个对象的属性,造成的无限循环
function cleanup(effectFn) {
for (let i = 0; i < effectFn.deps.length; i++) {
const deps = effectFn.deps[i]
deps.delete(effectFn)
}
effectFn.deps.length = 0
}
// computed 的实现
function computed(getter) {
let value // 缓存值
let dirty = true // true表示getter中的多个数据,改变了,需要重新计算
const effectFn = effect(getter, {
lazy: true,
scheduler() {
console.log("执行")
if (!dirty) {
dirty = true
trigger(newCompute, 'value')
}
}
})
const newCompute = {
get value() { // 第一个获取computed值
if (dirty) {
value = effectFn();// getter收集到各自的副作用函数
dirty = false
}
track(newCompute, 'value');// 用来收集计算属性的值的代理
return value
}
}
return newCompute // 第一次读取,触发
}
// 测试
// 定义obj为响应式数据
const obj=reactive({foo:1,bar:0})
const sumRes = computed(() => obj.foo + obj.bar);//注意sumRes不是响应式
console.log("computed1",sumRes.value)
console.log("computed2",sumRes.value);// 执行
obj.foo++ // 执行执行 trigger(newCompute, 'value') 如果没有下面effect,其实和computed没关系
console.log("computed3",sumRes.value);// 重新执行副作用函数,获取value,值改变
// 这个时候修改sumRes,不会触发任何效果,修改没有用
sumRes.value=11;
console.log("computed4",sumRes.value);// 重新执行副作用函数,获取value,值改变
effect(() => {
console.log(sumRes.value)
})
obj.foo++ // 这个时候修改obj,sumRes才会执行副作用函数才有效
///
// 实现watch
// =========================
// 递归读取对象所有的属性,触发track函数代理对象全部属性
function traverse(value, seen = new Set()) {
if (typeof value !== 'object' || value === null || seen.has(value)) return
seen.add(value)
for (const k in value) {
traverse(value[k], seen)
}
return value
}
function watch(source, cb, options = {}) {
let getter
// 如果是监听对象的函数,直接为getter,注意监听函数必须是读取数据
if (typeof source === 'function') {
getter = source
} else {
getter = () => traverse(source)
}
let oldValue, newValue
let cleanup
function onInvalidate(fn) { // 解决竞态问题
cleanup = fn
}
const job = () => {
newValue = effectFn()
if (cleanup) {
cleanup()
}
cb(oldValue, newValue, onInvalidate)
oldValue = newValue
}
const effectFn = effect(
// 执行 getter
() => getter(), // 副作用函数
{
lazy: true,
scheduler: () => {
if (options?.flush === 'post') { //表示其他副作用函数执行之后再执行
const p = Promise.resolve()
p.then(job)
} else {
job()
}
}
}
)
if (options?.immediate) {
job()
} else {
oldValue = effectFn()
}
}
// 异步测试函数
let count = 0
function fetch() {
count++
const res = count === 1 ? 'A' : 'B'
return new Promise(resolve => {
setTimeout(() => {
resolve(res)
}, count === 1 ? 1000 : 100);
})
}
let finallyData
watch(() => obj.foo, async (newVal, oldVal, onInvalidate) => {
let valid = true
onInvalidate(() => {
valid = false
})
const res = await fetch()
if (!valid) return
finallyData = res
console.log(finallyData)
})
obj.foo++
setTimeout(() => {
obj.foo++
}, 200);
//1.以上代码没有实现调度器,
// 调度器是指多次修改某一个对象的数据,只执行一次副作用函数(渲染函数),这上面的代码只要修改,就会触发渲染函数
//2.没有实现重写数组方法,代理数组,map,set等数据