携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天,点击查看活动详情
上章我们学习了如何处理数组中的一些遍历方法,这些方法大部分都是不会更改数组本身的,因此我们只需要处理一些边际情况就可以了,但是数组中还有一些其他方法会修改原始数据,比如最常用的push方法。
查阅一下ECMA文档
这个文档不难理解,其实就是push这个方法具体步骤,或者叫伪代码。书中作者关注的是执行步骤,我却更关注这个条件,这里表示如果数组的length加上本次插入的长度如果大于Float范围那么就会抛出异常.
从这个文档中,我们能大概看出push的执行流程
- 判断数组的length加上参数的length有没有超过范围
- 遍历参数,设置数组内的值,然后length+1
- return length
所以,我们可以看出,在push的时候,即会读取数组的length,又会修改数组的length,如果我们只是执行一次,应该不会出什么问题,但如果我们执行了多次,那就可以会爆栈,为什么呢?
我们假设这样一句代码,我们push了2次。
const arr = reactive([])
effect(()=>{
arr.push(1)
})
effect(()=>{
arr.push(2)
})
当我们push第一次,我们储存并追踪了一个读length的副作用和修改length的副作用。
这时候我们push第二次,根据之前的代码,我们会执行全部副作用,包括第一次的,但这时候我们其实本身是在执行第二次push,却又返回执行了第一次push的副作用,而执行第一次又会执行全部,又会执行第二次。
这时候我们可怜的length属性就会不停被循环嵌套的副作用搞到崩溃,从而爆栈。
知道问题所在自然就知道解决的方法,我们需要在push的时候禁止追踪,push完再追踪即可
let shouldTrack = true
const arrayMethods = {
push(...args){
shouldTrack = false
let res = Array.prototype.push.call(this,args)
shouldTrack = true
return res
}
}
const track = (target,key,receiver)=>{
if(!activeEffect && shouldTrack){
/*执行追踪逻辑*/
}
}
同理,pop、shift、unshift、splice这些方法都要这样处理,到这里,数组的响应就基本完成了。
下一章节会开始讲Set | Map的响应,这是ES6才有的对象,在vue2中,仅支持遍历,但是得益于Proxy,我们在vue3中也可以深度监听,明天继续一起看看如何处理吧