纯前端生成海报实践及其性能调优

1 需求背景

接到了一个紧急需求,需要根据 Excel 表格中学生的信息以及考试成绩生成相应的海报。

Excel 数据和需要生成的海报的样式如下:

Excel 数据
海报样式

由于需求紧急,没有时间拉上后端同学,所以 Excel 表格的数据解析和海报生成功能都需要由前端开发。

以下几个技术点需要关注:

1. Excel 可以通过 sheetjs来处理,通过在 XLSX.utils.sheet_to_json 将 Excel 中的数据转化为 JSON 格式数据。
2. 海报图片的生成可以先通过 html2canvas 将 HTML 转化成 canvas ,然后通过 canvas.toBlob 获得。
3. 最终通过JSZip 将图片打包进压缩包中。
4. 这里表单可配置项会比较多,因此我们需要一个配置导入导出功能,这里我们可以使用 FileReader 来实现表单配置导入,FileReader.readAsTextapi 能够读取文本的内容,更多用法可以参考 MDN FileReader。

在此基础上,确定此需求的总体开发思路:

1. 首先我们需要一个表单,获得海报可变文案的配置信息,例如不同题目考试成绩所对应的评语。

2. 遍历 Excel 中的每一条数据,根据每一条数据和表单的配置信息生成对应海报的 HTML 模板。

3. 根据 HTML 模板生成图片,并将图片数据保存进压缩包的对象中。

4. Excel 中的数据处理完后,下载压缩包,结束流程。

按照这个流程将功能开发完毕后,我在自己的机器上使用 100 条数据量的 Excel 表格进行测试,可以成功生成对应的压缩包,压缩包中的图片也没有问题,给运营同学演示后,她也表示很满意。

2 测试问题

但是当天晚上运营同学在自己的电脑测试这个工具时,悲剧发生了……

网页崩溃

在运营同学的电脑上,使用 15 条 Excel 表格数据生成海报时表现正常,当增加到 20 条 Excel 表格数据时,出现了网页崩溃的情况,提示 Out Of Memory。

3 分析问题

3.1 js内存问题

现在让我们来一起分析一下,在哪里出现了问题?

分析发现,最有可能出现问题的地方是步骤 3——最终通过JSZip将图片打包进压缩包中

压缩包对象所占用的内存在 Excel 表格数据处理完成并下载之前是不会被释放的,会一直增长。

所以我们有了一个简单的方案——分包。每处理 10 条数据就下载一次压缩包,将 JSZip (压缩包对象)所占用的内存释放。

但是事情真的有这么简单吗?20 张图片的数据以每张 1MB 的大小计算也才 20MB,怎么可能会导致网页崩溃呢?

凭空猜测没有作用,我们使用浏览器的 Performance 工具进行分析

网页内存增长情况 1

可以看到 JS Heap 在每处理一条 Excel 表格数据后都会增长,没有得到释放,这里没有得到释放的内存占用是上文分析的 JSZip 导致的吗?

继续使用浏览器的 Memory 工具进行分析

网页内存快照

可以看到,内存占用确实是一直在涨,没有得到释放,放大其中的一项。

网页内存快照详细信息

什么?竟然是 system.context ?作为一个前端开发,相信你看到这个词脑子里第一个冒出的念头应该就是上下文。检查代码,发现代码中使用了递归,所以造成了大量内存的使用,这里就不展示问题代码了。

将代码修改为循环语句后,再进行测试。

网页内存增长情况 2

可以看到内存的增长已经正常。让运营同学再次进行测试。

3.2 DOM 问题

问题就这样被解决了吗?

信心满满的找到运营同学进行测试,结果出乎意料,运营同学的电脑依然处理不了 40 条以上的 Excel 表格数据,而且在测试中还出现了一个问题,数据处理到某个阶段时,会卡住很久!

为了解决问题,我们继续进行分析。

排查内存溢出的问题可以从两方面入手——JS 和 DOM。既然 JS 的问题我们已经解决,那就看看 DOM。

整体流程中,对 DOM 进行操作的地方有两点:

1. 根据 Excel 表格数据生成对应海报的 HTML 模板。

2. 根据 HTML 模板生成图片。

第一点应该不存在内存溢出问题,因为我们既没有在 HTML 模版上添加事件,在处理下一条数据时也是直接覆盖上一次生成的HTML 模板,不会导致 DOM 节点不停增加。

继续分析第二点,我使用了第三方库 html2canvas ,由对应的节点生成 canvas 对象,之后由 canvas 对象生成图片的二进制数据。

根据 html2canvas 文档的指引,设置 removeContainer 属性保留其生成 canvas 对象时所克隆的 DOM 元素并查看。

DOM结构

结果出乎意料,html2canvas 完整的克隆了我们的 DOM 结构,除目标节点外还克隆了 React 的根结点,script 标签,link 标签。

此时,数据处理慢以及在处理某条数据时卡慢的问题就清楚了,由于 html2canvas 完整的克隆了我们的 DOM 结构,不仅复制了很多没用的节点,而且由于克隆了 script 标签,link 标签,还会发起网络请求下载相关的资源

我们需要把进行操作的节点插入在 body 标签下,根据文档指引,可以使用 html2canvas 提供的ignoreElements属性解决以上问题:

const canvas = await html2canvas(root, {
  imageTimeout: 10000,
  ignoreElements: (ele) => ele.id === 'root' || ele.tagName.toUpperCase() === 'IFRAME' || ele.tagName.toUpperCase() === 'SCRIPT' || ele.tagName.toUpperCase() === 'LINK',
});

通过以上代码,我们将无用的节点和会造成网络请求的标签进行了过滤。

优化过后,再让运营同学进行测试,这时处理一千条数据的 Excel 表格数据也不会再出现网页崩溃的问题了,同时处理速度也大大提升,1000 条数据在 4 分钟内可以处理完毕。

4 小结

回到最开始,JSZip 占用的问题依然存在,我们依然需要进行分包,不过分包的大小可以提升到1000条数据。

但是我们可以看到,如果不能找到问题的根本所在,一开始就进行分包也无济于事。

网页显示“喔唷,崩溃啦!”怎么办?请别着急,仔细分析才能解决问题。

紧追技术前沿,深挖专业领域
(0)

相关推荐

  • JavaWeb:Xml

    XML XML为什么出现? XML有两个先驱:SGML和HTML,这两个语言都是非常成功的标记语言,但是都有一些与生俱来的缺陷.XML正是为了解决它们的不足而诞生的. 先出现了SGML,然后又出现了H ...

  • (4条消息) H5的渲染流程笔记

    作者:JiajiaAz  来源:CSDN  原文:https://blog.csdn.net/qq_32657025/article/details/79569213 浏览器页面渲染流程 浏览器从HT ...

  • 浏览器指纹实现

    一.什么是浏览器指纹? ①.浏览器指纹是指仅通过浏览器的各种信息,如系统字体.屏幕分辨率.浏览器插件,无需 cookie 等技术,就能近乎绝对定位一个用户,就算使用浏览器的隐私窗口模式,也无法匿名.这 ...

  • 8个最好的SVG免费库

    所有现代浏览器都支持SVG文件类型,它很快就成为网站设计人员的首选. 您可以设计漂亮的图标作为支持向量机,并缩放到任何大小,而不损失质量.考虑到视网膜显示器的发展,这是SVG格式的最大优点之一. 如果 ...

  • 编程新手该如何调优程序?程序员必备性能调优利器——火焰图

    让我们回想一下,曾经作为编程新手的我们是如何调优程序的?通常是在没有数据的情况下依靠主观臆断来瞎蒙,稍微有些经验的同学则会对差异代码进行二分或者逐段调试.这种定位问题的方式不仅耗时耗力,而且还不具有通 ...

  • 软件测试项目性能调优的方法

    ### 重点:项目性能调优的方法 1.通讯 2.应用集群部署 3.缓存 4.资源动静分离 5.数据库集群(OraceRac) 6.SOA服务优化 1.1首先是一下通讯,通讯层面需要采取异步线程通讯模式 ...

  • Nginx安全优化与性能调优

    Nginx基本安全优化 隐藏Nginx软件版本号信息 一般来说,软件的漏洞都和版本有关,这个很像汽车的缺陷,同一批次的要有问题就都有问题,别的批次可能就都是好的.因此,我们应尽量隐藏或者消除Web服务 ...

  • 程序员必须练就的「性能调优」组合拳【2】

    性能调优系列前序文章索引: 程序员必须掌握的性能调优:老兵哥结合个人经历解释了程序员往架构师方向发展时为什么要跨越性能调优这一关,以及介绍了从 X.Y.Z 三个维度优化性能的思路. 从  X  维度优 ...

  • 程序员必须练就的「性能调优」组合拳【1】

    在[ 程序员必须掌握的性能调优 XYZ ]这篇文章中,老兵哥结合个人经历解释了程序员往架构师方向发展时为什么要跨越性能调优这一关,这是我们建立流程化.结构化.系统化的思维的契机.另外,老兵哥还介绍了从 ...

  • 性能调优-MySQL索引数据结构详解与索引优化

    本篇文章主要学习了MySQL的索引的数据结构的认识,做一个大概的了解即可. 一.索引 在关系数据库中,索引是一种单独的.物理的对数据库表中一列或多列的值进行排序的一种存储数据结构,它是某个表中一列或若 ...

  • 十八般武艺玩转GaussDB(DWS)性能调优:Plan hint运用

    前言 数据库的使用者在书写SQL语句时,会根据自己已知的情况尽力写出性能很高的SQL语句.但是当需要写大量SQL语句,且有些SQL语句的逻辑极为复杂时,数据库使用者就很难写出性能较高的SQL语句. 而 ...

  • Linux性能调优 | 01 平均负载的理解和分析

    Linux性能调优 | 01 平均负载的理解和分析

  • 部署 Node.js 应用以完成服务器端渲染 Server Side Rendering 的性能调优

    原文:Operationalizing Node.js for Server Side Rendering 在 Airbnb,我们花了数年时间将所有前端代码稳定地迁移到一致的架构中,在该架构中,整个网 ...