Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于首次渲染卡顿以及watch监听被反复触发的性能优化 #2

Open
buuing opened this issue Jun 30, 2020 · 0 comments
Open
Labels
enhancement New feature or request

Comments

@buuing
Copy link
Owner

buuing commented Jun 30, 2020

第一版: 深度监听data.children

因为考虑到树是从上到下逐级生成的, 如果使用v-if让子节点不渲染, 那子节点的watch和computed也就不会创建, 所以刚一开始的时候, 我的思路是使用watch深度监听data.children, 外加v-show使得子节点在初始阶段只渲染一次, 后续的展开折叠也属于高频操作, 也有利于节省性能

但是这种情况到了应用时, 才发现如果监听的是data.children, watch也会被该节点的无用属性触发, 比如展开折叠改变的expand属性也属于data的一部分, 也就是说树插件的每一次操作都会触发一堆深度监听的watch, 即便这些操作是与checked不相关的, 还有就是由于使用v-show, 首次渲染时要创建所有节点, 测试4000+单节点的首次渲染时间高达4.5s左右, 导致js程持续占用GUI渲染线程


第二版: 单独监听data.checked外加$emit向上传递变化

之后仔细研究element的tree源码之后, 我发现他是单独监听的data.checked, 然后通过$emit逐级向上触发一个函数, 函数内通过js向下探知子节点的选中状态来改变自身, 这样做的好处是, 不再需要担心节点其他无用属性的干扰触发, 但是当我再次应用后发现, watch被触发的频率会随着节点的深度增加, 会裂变非常恐怖

比如树结构有6层节点, 现在我让其中一个节点选中, 当前节点观察到变化向上触发一次$emit, 然后下面的节点也会被全部被选中, 同样触发一次子节点的watch, 然后又一次的向上$emit, 就像是水波纹一样触底之后逐渐扩大, 尤其是节点增加到4000+的时候, 点击一次checked的延迟非常高

其实这个问题在第一版监听children的时候也存在, 因为是深度监听, 所以也就是触发的顺序略有不同罢了, 下图为深度监听的触发

20200702103457


第三版: 保留$emit向上传递, 弃用watch监听, 改用原生js通过前序和层序向下遍历

问题就是处在watch在监听时会重复触发$emit, 那么解决方案也只能是放弃掉watch, 每一次的checked改变分为两步走, 一是继续通过$emit向上传递事件, 二是使用原生js向下提前感知节点变化, 通过递归每次只改变当前节点, 前序遍历半选状态, 层序遍历叶子节点状态, 得到结果提前打断函数来节省性能

好处是子节点不再新建watch, 可以放心的使用v-if优化首次渲染问题, 把海量节点的渲染分摊到用户的每次点击操作, 目前测试4000+的单一树节点也运行流畅, 缺点是以后所有封装改变checked的方法都严重依赖原生js


最终和 el-tree 的性能对比

computed-tree

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant