《VueJS设计与实现》5.7 代理数组(上)

90 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情

数组,一直是我在vue中使用最多的数据结构,当我们使用v-for的时候,对应的数据也基本是一个数组,因此代理数组在vue中很重要,今天我们就跟随作者,一起来了看看如何代理数组。

在我们之前曾经讲过:常规对象异质对象,并且说数组是最常见的异质对象.我们对比一下数组的特殊之处。

  • 通过下标读取和修改值,arr[0]=1
  • arr.length时返回数组的长度,修改arr.length会导致数据的变化
  • 可以使用for ... infor...of遍历
  • 原型链上自带很多方法,比如filterslicepushshift,其中有改变自身的,也有不改变的

从这四点可以看出,数组与对象差别还是蛮大的,但是对于我们写的代码,其实差别并不是很大,因为数组也只是一个对象而已,我们之前写的代码还是可以复用大部分的。

首先我们先看一下如何响应数组下标的变化。我们给数组中不存在的元素赋值,那就是新增,反正就只是更新。 比如arr.lenght = 2;arr[2] = 2,在判断数组类型上,我直接使用es6的新API,Array.isArray.

const TriggerType = { SET:'SET', ADD:'ADD', DEL:'DELETE' }

const createReactive = (obj,deepify = true, onlyRead=false)=>new Proxy(obj,{
    set(target,key,newVal,receiver){
       /*省略其他逻辑*/
       const type = Array.isArray(target)
           ? Number(key)<target.length ? TriggerType.SET : TriggerType.ADD
           : Object.prototype.hasOwnProperty.call(target,key) ? TriggerType.SET :TriggerType.ADD
           
    }
   /*省略其他拦截*/
})

这样我们就实现了,对数组赋值的响应。但是,数组中还有一个特殊的length属性需要处理。在追踪的时候我们无需修改,仍将它当作一个普通的属性去处理。

const trigger = (target,key,type)=>{
    /*省略其他逻辑*/
    if(type === TriggerType.ADD && Array.isArray(target)){
        const lengthEffects = depsMap.get('length')
        lengthEffects&& lengthEffects.forEach(fn=>fn!==activeEffect?effectToRun.add(fn):'')
    }
}

当我们修改length的时候,并不意味着需要响应数组中所有的元素,我们应当只响应大于等于新length的元素。

所以我们需要将新的length传进去,在trigger中进行判断。在触发响应的时候,我们能拿到新的length值,这时候将它传入trigger作为对比。

//新增newVal这个参数
 const trigger = (target,key,type,newVal)=>{
     /*省略其他逻辑*/
     if(Array.isArray(target) && key === 'length'){
          depsMap.forEach((fns,key)=>{
              if(key>=newVal){
                  fns.forEach(fn=>fn!==activeEffect?effectToRun.add(fn):'')
              }
          })
     }
 }

我们为trigger增加了第四个参数,newVal就是新的length值,它表示数组的长度发生了变化。当我们触发响应式,如果数据是数组,则找到所有下标大于等于新length值的元素,再执行与他们相关的副作用函数。

下一章我们开始讲数组的遍历,这也是数组中比较重要的一部分。