FXFX.GRID

有关于vue数组响应式的记录

最近在做的一个项目中的筛选模块,首先该模块的筛选输入可能会需要考虑到至少深度为两层的树。每个节点我们姑且称为TreeNode,而整个Tree如下

root [
  // 第0层,也就是所有可以筛选的项
  {
    key:foo,
    // 第1层,奴属于该项的所有“可选项”或者“选项”
    child:[
      {
        key:bar,
        // 第二层,该项“选项”
        child:[{}]
      },
      { key:foo2 }
    ]
  }
]

在阅读了部分naive-ui的源码后,决定尝试使用 treeMate 来生成树的元数据,然后使用其提供的方法进行checkedunchecked操作。

#TreeMeta

创建元数据的方法比较简单。根据文档,只需要调用createTreeMate即可。

import { createTreeMate } from "treemate";
createTreeMate([
  {
    key: "foo",
    children: [
      {
        key: "bar",
      },
    ],
  },
]);

这样我们就获得了整个筛选组件树的元数据了。

#难题

但是,我们可以发现,在选项中,有些许内容并不是一开就有的,也就是说,选项内容是异步获取的,这包括

  1. 数据来源于后端的某个接口。
  2. 数据存在组件(data)中。
  3. 数据存在vuexpinia等状态管理工具中。

这时候就可能涉及到以下问题。

  1. 如何更新筛选组件中的内容(触发重新渲染问题)
  2. 对于一个封装好的组件而言,createMateTree的数据来源必然是通过props传进来的,那当props更新时,是否需要重新生成treeMate
  3. 倘若treeMate重新生成,原来的勾选状态是否会丢失?

#问题一

这一问题可以通过一个巧妙方式解决。那就是只有当用户点开一个选项时,我们才去渲染筛选内容。这样做不但能保证在组件渲染时需要的数据已经完全加载,而且还能在一定程度上减少请求的数量,什么意思呢?

试想,如果有一个选项中数据是异步获得的,那么从逻辑上来说,若用户不需要进行这个选项的筛选,那么无论是对于用户,还是组件本身都不需要加载这一部分数据。这就很自然的导出一个结论,只有当用户点击需要异步获取的选项时,我们才去加载这一部分数据,能加载完成后再渲染组件内容。这就是 当用户点开一个选项时,我们才去渲染筛选内容 的意思。

那我们如何去实现这个事情呢?借助ES6Promise,我们可以优雅的完成这个想法。

TreeNode中我们增加一个参数remote,这个参数需要返回一个Promise,且Promise的返回值必须是一个符合规格的TreeNode。这样,当我们需要打开这个筛选选项时,只需要判断一下当前节点是否有remote,且这个remote是否是要给Promise即可。如果是,执行这个Promise,成功后把数据(符合规格的TreeNode)插入筛选的选项树中,打开筛选面板。任务就完成了。

#问题二

根据问题一可以知道,在用户点击选项后,数据可能获得了更新,这个原先的treeMate必然无法覆盖到新的数据,我们需要再次调用createTreeMate生成新的元数据。

#问题三

我们知道,在整个树中,每个选项需要拥有自己的,独立的,唯一的标识。它就是key。关于key我们可以有多种实践的方案。

  1. 主键自增(不是
  2. 使用ES6的新类型Symbol
  3. 使用命名空间

方案 1,方案 2 的优点就是容易书写。只需要几行代码就完事了。但随之而来又会带来新的问题。key自增和使用Symbol的方式在每次重新生成元数据后,已选的选项状态将会丢失。所以,我们需要一种更加稳定的,无论何时,何地重新生成元数据,同一几点的key都必须相同的方法。这个方法便是使用命名空间。

FXFX.THEME