Google Gemini生成的摘要
注: 本文基于 univer-sheet 源码,对其复制粘贴解析逻辑进行核心优化解读。
效果图:
变更前:
变更后:
由上图可以看到在提交变更之前,粘贴解析长任务耗时 27.5 秒并且内存没有得到回收,出现了内存泄漏问题。在变更之后,耗时仅需要 2.68 秒,对应的内存也得到释放。
注: 内存泄漏问题也可以通过堆快照定位。
PR 请求可以 点此查看。
耗时原因分析:
通过开发者工具结合源码分析,我们能发现 windows.getComputedStyle().getPropertyValue
出现了大量耗时的情况。
为什么 getComputedStyle()
和 getPropertyValue()
方法会大量耗时造成页面卡死?
强制重排(reflow):
getComputedStyle()
方法会导致浏览器计算元素的所有样式,这可能需要重新计算整个文档的布局。这是因为浏览器需要确保样式是最新的,并且在一些情况下可能会重新布局页面。这种重排操作是非常耗时的,特别是当页面上有大量的元素时。同步操作:
getComputedStyle()
方法是同步的,这意味着浏览器必须在返回结果之前完成所有的计算。这会阻塞主线程,导致页面的其他操作变慢或卡顿。布局树的生成: 浏览器需要生成和更新布局树(layout tree),以便计算每个元素的最终样式。这些操作通常非常复杂,涉及大量的计算和内存操作。
复杂的 CSS 规则: 如果页面中有大量复杂的 CSS 规则,或者样式表层级嵌套较深,浏览器计算每个元素的最终样式时会更加复杂和耗时。
解决方案
DOM 树是树结构,我们可以采用深度优先遍历的方式,将上层样式传递到下层节点模拟计算样式,来避免使用 getComputedStyle
。节点样式是通过样式选择器的优先级,来确定最终的样式。了解这两个基础逻辑后,我们就可以开始编码了。
解析 style 标签,将标签的样式存储在 Map 中
1 |
|
注: style 标签只有挂载到 DOM 上,才会实现
CSSStyleSheet
接口。而使用 shadow DOM 的目的是为了样式隔离,避免造成全局样式污染。
获取样式函数
1 |
|
详细函数实现请翻阅
packages/sheets-ui/src/services/clipboard/html-to-usm/converter.ts
。函数实现时需要注意在各类 html 中,例如background
、background-color
在表格中均可代表背景颜色,应该补充边界处理。
内存泄漏问题
参考 StackOverflow 上的 这篇帖子。
1 |
|
在解决内存泄漏时,剔除了 DOMParser API 的使用,并去除了将 html 字符串挂载到 DOM 上的行为。在粘贴行为结束后的调用dispose函数回收解析过程中使用的 Map 和临时变量。经过这样处理,不仅解决了内存泄漏的问题,还节约了挂载构建 DOM 树的时间。