优化递归获取树形数据太慢_记一次React Native FlatList的优化

前几天公司要做Demo展示, 业务的一部分使用RN Web做的, 其中有个需求是以树形结构展示用户各个项目的进度, 如图所示:

6c9841bf10dce108a2a55ea2ba61e7c0.png

之前的同事是把这些条目按照顺序放到一个数组里(其实就是把一个树形结构压扁), 例如:

[父亲1,儿子11,孙子111,孙子112, 儿子12,孙子121,孙子122,孙子123,父亲2...]

然后把这个数组作为data传递给FlatList组件,依次渲染里面的component。

但问题是,数组里的条目有很多, 多层的嵌套关系导致元素有2000多条,当快速滚动FlatList时,尝尝出现白屏,需要等待1-2秒才能渲染成功。另外在父子层级之间折叠/展开的时候,由于要多次重新渲染,卡顿十分明显。另一个突出的问题是,由于数据结构是扁平的数组,很难区分父子元素,在处理折叠/展开的逻辑时, 需要很多额外的数据去做管理,逻辑不易处理。

优化的第一点:首先是把数据结构由扁平的数据转换为树形结构,利用children: []属性存储当前节点的子节点,从而更容易的表示出层级关系。转换函数如下:

listToTree 

根据上面的例子,转换后的树形结构如下:

const 

优化的第二点: 有个以上这个属性结构,我们就更容易的渲染出层级关系。但有一点我们要想到,就是父子的层级数量可能是不确定的,比如:

父亲 -> 儿子 -> 孙子

但也有可能是

父亲 -> 儿子 -> 孙子 -> 曾孙子

在我们的业务中,除了叶节点,也就是最后一个节点会使用TaskRow来渲染外,其他往上的父节点都会用LevelRow来渲染。为了适应结构层级的变化,我们需要一个组件能够嵌套渲染,也就是说它首先会渲染一个父节点(LevelRow),如果父节点有children,再以child为父节点,递归的使用该进行渲染,直到遇到了叶节点,则不再递归,而是渲染TaskRow。这样,我们即实现了这个树形结构的渲染过程,无论都多少层 都没关系。代码如下:

// @flow

用上述方法优化后,传入FlastList的data数据,从一个大小为1000多的数组,通过listToTree变成了只有6个根节点的数组(其他节点都以children属性的方式,从根节点开始嵌套引用)。而后,将这6个根节点依次用LevelWrapper渲染,即完成了从一个根节点出发,递归渲染至叶节点的过程。而每一层父子之间,通过LevelWrapper的showChildren 状态,可以很容易的控制父子之间的折叠/展开。

<

而每次的折叠/展开,改变的只是这一层下LevelWrapper组件的状态,其他位置的LevelWrapper并不会重新渲染,这就会大大提高FlatList的渲染效率,毕竟它不要每次都重新渲染1000多个组件。而在滚动FlatList的时候,它的数据源从一个1000长度的数组变为了只有6个元素的数组,也大大提高了FlatList的管理效率(虽然我对FlatList具体怎样管理也不是很清楚,但这样做确实是大大提高了渲染效果,白屏的问题也消失了)。

以上就是此次优化的主要内容。

补充: 除了优化FlatList外,还有一个功能就是能够根据关键字,搜索出需要的条目。根据文章开头的例子,比如用户搜索 ’孙子121‘,那么就应该展示出以下树形结构:

父亲1 -> 儿子12 -> 孙子121

这其实就是一个树形结构的搜索问题,我们业务中要求的结果就是:如果节点本身或者其子节点包含搜索内容,则该节点,该节点子节点,以及其父节点都要显示出来,换句话说就是只要树形结构中的任意一个节点满足要求, 那么这个节点所在的分支路径要完整的展示出来,效果如下:

3afc1aac83299a669c87fb3509f776e6.png

当搜索’wall setout‘时,它的结果是一个叶子节点,则其父节点 L1-xxx 和 East Tower都要展示出来,这样用户才能知道这个结果处于哪些条目下.

43547f3d911501cbae01b56c8220c20e.png

当搜索’L1‘时,它是一个中间节点,则其父节点 East Tower和其余的子节点都要展示出来。

以上的要求,其实就是要根据搜索内容,对listToTree的结果做一次修剪,如果树形结构中的某个节点及其子节点都不满足条件的话,则将整个分支从树形结构中剪去。再次利用递归的知识,代码如下:

// 传入一组节点,检查节点及其子节点中是否有满足要求的,若有则返回true

以上就是本次工作的主要内容

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: MySQL实现递归查找树形结构可以通过使用WITH RECURSIVE和UNION ALL语句来实现。以下是一个示例查询: 假设我们有一个名为“category”的表,其中包含以下列:id、name和parent_id,其中parent_id列包含一个指向同一表中的父类别的外键。 我们可以使用以下查询递归地查找树形结构: ``` WITH RECURSIVE category_tree (id, name, parent_id, depth) AS ( SELECT id, name, parent_id, 0 FROM category WHERE parent_id IS NULL # 根节点 UNION ALL SELECT c.id, c.name, c.parent_id, ct.depth + 1 FROM category c JOIN category_tree ct ON c.parent_id = ct.id ) SELECT id, name, parent_id, depth FROM category_tree; ``` 在此查询中,我们首先选择根节点,然后使用UNION ALL语句递归地选择所有子节点。使用WITH RECURSIVE语句和递归查询,我们可以轻松地构建树形结构,并根据需要进行任何进一步的处理。 ### 回答2: MySQL是一种关系型数据库管理系统,它本身并不支持递归查询,但可以通过多表联接和递归实现树形结构的查询。 在MySQL中,可以使用两种方式来实现树形结构的递归查询:使用递归函数或使用临时表。 使用递归函数可以实现树形结构的递归查询,该函数可以通过递归的方式查询树形结构中的子节点,并将结果集逐层汇总。递归函数通常包括两部分:基准查询和递归查询。基准查询用于获取初始节点的直接子节点,递归查询则用于获取每个子节点的子节点,依次类推。通过将基准查询和递归查询结合起来,并使用UNION ALL将结果集合并,就可以得到完整的树形结构。然后可以使用JOIN等操作将查询结果与其他表进行联接,实现更复杂的查询功能。 另一种方式是使用临时表来实现树形结构的递归查询。首先,创建一个临时表,其中包含树形结构的节点和其父节点的信息。然后使用一个循环将树形结构中的节点逐层加入到临时表中,直到所有节点都被添加进去。最后,使用JOIN等操作将临时表与其他表进行联接,实现需要的查询功能。 无论使用哪种方式,实现树形结构的递归查询都需要注意两个问题:首先是递归的结束条件,即确定递归何时停止;其次是性能问题,树形结构可能非常复杂,递归查询消耗的资源和时间都较大,需要综合考虑查询效率和系统性能。 总的来说,通过合理地使用递归函数或临时表,结合适当的查询操作,可以在MySQL中实现树形结构的递归查询,并完成需要的查询功能。 ### 回答3: MySQL是一种关系型数据库管理系统,它本身不支持递归查询和树形结构的存储。但是,我们可以利用MySQL的一些特性来实现递归查找树形结构。 实现递归查询树形结构的方法有很多种,以下是其中一种常用的方法: 1. 为每个节点添加一个字段,用于录节点的父节点ID; 2. 创建一个递归查询函数,用于查找节点的子节点,并将结果保存到一个临时表中; 3. 递归调用该函数,直到找到所有节点的子节点; 4. 使用JOIN语句将各个临时表连接起来,得到完整的树形结构。 具体步骤如下: 1. 创建一个表来存储树形结构的节点信息,表中包含节点ID、节点名称和父节点ID等字段; 2. 创建一个存储递归查询结果的临时表; 3. 创建一个存储递归调用所需参数的存储过程或函数,参数包括父节点ID、递归深度和临时表名等; 4. 在存储过程或函数中,使用SELECT INTO语句查询满足条件的子节点,并将结果插入到临时表中; 5. 在存储过程或函数中,使用递归调用语句调用自身,并传递子节点作为父节点ID; 6. 在存储过程或函数中,使用条件判断语句来停止递归,例如判断是否已经到达最深层级或没有更多子节点; 7. 在主程序中,调用存储过程或函数,并使用JOIN语句将各个临时表连接起来,得到完整的树形结构。 通过以上步骤,我们可以利用MySQL的一些特性,实现递归查询和树形结构的存储。但需要注意的是,这种方法可能会对数据库性能造成一定的影响,因此在实际应用中需要根据具体情况进行优化和调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值