最近做浏览器打印需求,总结了两篇相关的博文:《浏览器打印知识扫盲》和《浏览器打印方案调研》,最后确定了浏览器打印的方案实现,也封装了自己的easy-print npm 包,解决的问题有:
- 浏览器打印不能自定义分页行为;
- 浏览器打印不能个性化定制打印内容;
- 浏览器打印样式问题,不同打印机打印结果不一致;
但是实际应用这种方案到我们的系统中之后,面对大批量的打印,连续200张的打印就会使系统直接奔溃,页面没渲染出来,浏览器无响应了。。。
问题背景
我们的系统有批量打印的需求,所以经常需要批量打印,所以就出现了一次打印 200 张、300 张的场景,如果使用的电脑系统好一点,可能还不至于浏览器卡死无响应,但是用户那边的电脑配置参差不齐,就会出现性能问题。
于是找到加载多张导致浏览器奔溃的原因就很重要,这里要感谢一下 谷歌浏览器给我门提供的工具了,chrome 开发者工具中的 Performance 面板。
利用 chrome 开发者工具中的 Performance 面板, 我们可以记录一段时间内浏览器的加载渲染过程,会有渲染的分析结果可以帮助开发者去了解页面渲染的背后发生了什么,具体操作大家网上一搜线下一试就会用了。
这里我也是使用 Performance 面板 来定位原因的。
当时一次性打印200页的渲染结果截图如下:
紫色:样式计算、布局计算、渲染用的时间;
黄色:脚解析本计算、dom操作的时间;
可以看到 200 张加载的时间是 1min,其中 黄色和紫色 的时间消耗是最大的。
问题分析
分析上面的结果,其实原因就很明确了:
- easy-print 生成 打印文档内容,底层依赖的是 jquery 操作dom, 不断的 append。。。我们知道 jquery 的设计思想就是简化 dom 操作,就是我们写起来简单,codeing time 减少了,但是浏览器真正处理起来却会慢,和原生dom操作的性能消耗差不多。所以一次加载多张势必会多次操作dom,消耗过大;
- 小白 使用 easy-print 解决了不同打印机上打印结果不一致的问题,是因为打印样式书写都采用绝对定位,每个元素都会有宽高、大小、位置的样式规定,就是样式的规定很细,每一张文档内容,每一个元素都需要计算布局、进行重排渲染,所以样式计算、布局计算,渲染时间开销不会少;
可是怎么解决这个性能问题呢?说实话小白当时是有点懵的!!
样式太多,计算开销大,可是正因为有这些样式,我们才能摆正浏览器打印在不同打印机上显示的一致性啊!! Jquery 操作 dom,太耗浏览器性能,这个是因为 hiprint 库底层就是依赖 jquery 实现的!!
当时想了很多解决方案:
- 考虑到实现打印的组件是 hooks 函数组件,hooks组件使用的是闭包思想,会有想不到的内存开销,就把 hooks 函数组件换成class 组件。结果是优化不明显;
- 考虑到是一次性加载太多张文档内容,导致开销太大,所以改成懒加载,但是这并不能真正解决性能问题;
- 当一次打印太多的是时候,使用分批打印,但是需要首先计算有多少页打印内容,然后按照一次性打印的张数极限分成几批打印,也不是真正解决性能问题的方法;
- 考虑到 jquery 操作 dom性能开销太大,跟项目中vue/react 的虚拟dom 的概念是背道而驰的,所以优化思路就是使用 react dom 替换 jquery dom;
问题解决
使用 react dom 替换 jquery dom,小白在 easy-print 里面增加了一个新的 api:
hiprint.getReactDom(panel, printData);
其中:panel 是你的打印文档内容面板,printData 是批量打印的文档数据。
300张 同样文档的测试结果如下:
替换成 react dom 之后可以说,速度翻了好多倍,这就是小白用实力证明了虚拟dom 的性能有多好!!!!
这个react dom api 已经发不到 easy-print 的最新版本中,附带的有使用的demo,感兴趣的可以关注看看哈。该 api 目前还没有对接全部的hiprint api,表格的部分还没有实现,后续会持续更新上去~
Ps:在小白测试的过程中,还发现了一个事情,如果你的文档内容只有 文字和图片的,加载速度还会更快,如果里面有条形码、二维码这种,就会增加很多渲染开销。尤其是 二维码,不同的生成方式,消耗还不一样,svg 比 canvas 的耗时要更久,但是 svg 比 canvas 打印出来要更加清晰,涉及到的小伙伴们可以根据自己的需要选择。
欢迎小伙伴可以点赞哈!!!!