ant-design Tree 的 titleRender 刷新多次

qc6wkl3g  于 4个月前  发布在  其他
关注(0)|答案(7)|浏览(69)
  • I have searched the issues of this repository and believe that this is not a duplicate.

Steps to reproduce

展开/折叠时刷新titleRender多次。

What is expected?

减少不是必要的titleRender刷新

What is actually happening?

减少不是必要的titleRender刷新
| Environment | Info |
| ------------ | ------------ |
| antd | 4.18.8 |
| React | react |
| System | windows |
| Browser | Chrome |

3yhwsihp

3yhwsihp1#

Hello @meijuji202. We totally like your proposal/feedback, welcome to send us a Pull Request for it. Please send your Pull Request to proper branch (feature branch for the new feature, master for bugfix and other changes), fill the Pull Request Template here, provide changelog/TypeScript/documentation/test cases if needed and make sure CI passed, we will review it soon. We appreciate your effort in advance and looking forward to your contribution!

你好 @meijuji202,我们完全同意你的提议/反馈,欢迎直接在此仓库 创建一个 Pull Request 来解决这个问题。请将 Pull Request 发到正确的分支(新特性发到 feature 分支,其他发到 master 分支),务必填写 Pull Request 内的 预设模板 ,提供改动所需相应的 changelog、TypeScript 定义、测试用例、文档等,并确保 CI 通过,我们会尽快进行 Review,提前感谢和期待您的贡献。

gmxoilav

gmxoilav2#

有解决方案了吗?

mzillmmw

mzillmmw4#

title 一共被渲染了三次,多余的两次渲染都是因为 serRef/children 变化导致。

如果不考虑实现与功能,多余的两次渲染是 rc-virtual-list 引起的。
示例代码

可以从上图中看出,多余渲染的原因是一系列对 resize 的监听操作引起,第一次渲染引起了高度变化,导致了后续两次 "多余" 的渲染。

不过更为尴尬的是,上述示例代码中设置了 virtual false,也会出现一样的问题。

顺着线索,来看下 rc-virtual-list 的实现。

先看下这个 Filter 的多余渲染是否合理

Filter 中 onInnerResize 参数的来源

collectHeight 的来源

可以看到这个参数的最终来源是 useHeights 这个 hooks。

同时可以从这段代码中看出,这个 function 并没有依赖其他变量,但是没有做缓存,导致每次都返回了一个不同的实例。

接下来看看这个 children 上的变化是什么引起的,来到使用 ResizeObserver 的地方,Filter 的实现里面。

这里的 onInnerResize 看着有点眼熟,它就是上面那个没有做好缓存的函数,这里又被简单封装了依稀,同时也没有做好缓存。

接下来看看这个 children 是哪些东西。
可以从这里看到,这个 children 是来自 useChildren。

可以从这里看到 ,useChildren 每次执行都会返回一个新的数组,导致了 children 变化。

这个 useChildren 的结果能被缓存吗?我们来看一下它依赖的变量都是来自哪里。

  • list 数据来自变量 mergedData,最终来自 rc-tree 中的 Tree.state.flattenNodes,考虑到这里是 expand 操作,且示例中的 props 均未发生变化,在这个场景下变化是由于 Tree.setExpandedKeys 方法调用引起的变化
  • startIndex 来自这个 memo 的结果,标明了虚拟列表需要从哪一个开始渲染
  • endIndex 同上,标明了虚拟列表需要渲染到哪一个
  • setNodeRef 来自 useHeights 的返回值,这个函数依赖了 onItemAdd/onItemRemove,但是看调用的地方,传了两个 null,其实在这个场景下是不会变的
  • renderFunc 其实就是 rc-virtual-list 的 children,这里用了 render props 的写法,同时没有做好缓存,导致每次传入的函数实例都会变化
  • { getKey }: SharedConfig SharedConfig 来自这个部分,getKey 函数的加了缓存,但是 SharedConfig 对象的生成没有缓存

可以看到,上述依赖在 props 不变的场景下,如果能做好缓存,是不会出现额外渲染的。
到这里,可以简单总结一下,基本上是由于 rc-virtual-list/rc-tree 中的一些 fucntion 没有做恰当的缓存导致。

gojuced7

gojuced75#

分析到这份上了,不来个 PR?

kmpatx3s

kmpatx3s6#

书接上回

当我们改进上面提到的部分后 ,会发现还是有一次额外的渲染

treeNodeRequiredProps 比较好处理,只是每次创建了一个新对象,简单用 useMemo 包裹一下就能解决。

但 motionType 的问题就比较复杂,需要看一下它为什么会产生变化。

查阅 rc-tree 中 NodeList 的实现,可以看到是 expandedKeys 变化导致了 motionType 从 null 变成了 show。

接下来看一下 MotionTreeNode 中是怎么消费 motionType 的,可以看到对 motionType 有依赖的两个属性 motionAppear/visible,只在 motionNodes 存在时会被使用到,且不会被 rc-tree TreeNode 消费,那么就可以用 memo 简单包裹 TreeNode 来缓存结果。

最终处理之后,我们就能得到一个 title 只渲染一次的代码

hivapdat

hivapdat7#

问题依旧存在,请问还有后续么?

相关问题