Vue 设计与实现-响应式部分总结

看的后面有的没看懂, 于是总结下之前的, 思路连贯, 就清晰了

初设计

无响应式却更新

obj.ok ? obj.text : 'not'
obj.ok = false

依赖应和 obj.text 没什么关系

所以, 每次调用 effect 函数的时候, 首先清除依赖, 如下方法

function cleanup(effectFn) {
  for (let i = 0; i < effectFn.deps.length; i++) {
    const deps = effectFn.deps[i]
    deps.delete(effectFn)
  }
  effectFn.deps.length = 0
}

set 循环

const set = new Set()
set.forEach(item => {
  set.delete(1)
  set.add(1)
	console.log('死循环中......')
})

解决

const set = new Set()
const newSet = new Set(set)	// Set 接受一个可迭代对象
newSet.forEach(item => {
  set.delete(1)
  set.add(1)
	console.log('不会死循环了')
})

所以, trigger 中增加代码: const effectsToRun = new Set(effects)

effect 嵌套

effect(() => {
  console.log('effect1 run')
  effect(() => {    // 该函数后执行, 因此会覆盖 activeEffect
    console.log('effect2 run')
    obj.foo // 修改, 全打印
  })
  obj.bar  // 修改, console.log('effect2 run')
})

解决 effect函数下

const effectFn = () => {
  cleanup(effectFn)
  activeEffect = effectFn
  effectStack.push(effectFn)
  const res = fn()
  effectStack.pop()
  activeEffect = effectStack[effectStack.length - 1]
  return res
}

无限递归

effect(() => {
  obj.foo = obj.foo + 1
})

读取 track 修改 trigger 刚刚 tack 的函数 即 obj.foo = obj.foo + 1

track => trigger => track => trigger …

所以, 在 trigger 下的 forEach 中

if (effectFn !== activeEffect) {
  effectsToRun.add(effectFn)
}

代理对象

代理对象键
  1. in => has

  2. for/in => ownKeys for/in 的目的是迭代对象的键 所以, tarck 时候的 key 为 ITERATE_KEY

通过类似 obj.foo = 123 方式, 有两种可能的情况

  • 键已存在, 修改值
  • 添加一个新的键值对

如果是第一种情况, 不做处理, 因为对 for/in 迭代出来的键没有影响

第二种情况, 在 set 的时候判断 Obejct.prototype.call(target, key) ? 'SET' : 'ADD' 该 key 是否存在在 target 上 是的话, 执行 key 为 ITERATE_KEY 的副作用函数

  1. delete => deleteProperty
代理对象值

因为有如下可能的情况

NaN === NaN		// false

在 set 中有

set(target, key, newVal, receiver) {
  const oldVal = target[key]
  if (oldVal !== newVal && (oldVal === oldVal || newVal === newVal)) {
    trigger(target, key, type, newVal)
  }
}

继承二次触发

const child = reactive({ child: 1 })
const parent = reactive({ parent: 2 })
Object.setPrototypeOf(child, parent)
effect(() => {
  console.log(child.parent)
})
child.parent = 3  // 打印两次 3

set(target, key, newVal, receiver) 中 target 为原始对象, 是变化的 而 receiver 为代理对象, 是不变的

所以 get 中 if (key === 'raw') return target

if (target === receiver.raw) trigger

代理数组

代理 length
arr.length  	// 响应数据
arr[2] = 123	// 添加数据

set 中

Number(key) >= target.length type = ADD

trigger 中 遍历 depsMap.get('length').forEach(f => f())

if (type === 'ADD' && Array.isArray(target)) {
  const lengthEffects = depsMap.get('length')
  lengthEffects && lengthEffects.forEach(effectFn => {
    if (effectFn !== activeEffect) {
      effectsToRun.add(effectFn)
    }
  })
}
代理单个数组数组 arr[0]
arr[2] = 123		// 响应数据
arr.length = 0  // 删除数据

trigger 中 递归 arr 下的所有 key所有 set

if (Array.isArray(target) && key === 'length') {
  depsMap.forEach((effects, key) => {
    if (key >= newVal) {	// 代理数据自身的 key	(如上例中的 2)	>=	0	(arr.length 设置的值)  说明此时数据已经删除
      effects.forEach(effectFn => {
        if (effectFn !== activeEffect) {
          effectsToRun.add(effectFn)
        }
      })
    }
  })
}
for/in
arr[1] = 123		// add	
arr.length = 0	// delete

上述本质上都是修改数组 length 属性

所以, 如果 for/in 为数组, 执行之后, 触发 length 的 trigger

ownKeystrack(target, Array.isArray(target) ? 'length' : ITERATE_KEY)

for/of

get 中 typeof key !== 'symbol' 防止使用 length 和 Symbol.iterator 比较

effect(() => {
  for (const val of arr) {
    console.log(val)
  }
})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值