后端返回数据让前端做导出
业务背景
如果后端只返回数据让前端做导出,那么数据量多会导致页面卡到假死状态,js是单线程,循环处理数据的同时页面会卡死,不用说用户,就连自己也会受不了,所以就可以使用webWorker开多一个进程来处理数据。
在前端对透视表数据导出为excel
进行下载,透视表数据最高可到百万个单元格以上,因此在主线程内对大量数据进行excel
导出时不可避免的会对主线程进行阻塞,而主线程阻塞对于用户感知来说就是页面卡顿;
显然,这种业务是一种典型的CPU
密集型任务,如果可以放在主线程以外的线程进行执行那么就可以有效的避免对主线程的阻塞了;Worker 正是为此而生,通过创建worker
来新建线程,就可以有效的分担主线程的压力;
关于 Worker
至于worker
的使用,可以参考这个——使用 Web Workers - Web API 接口参考 | MDN,其实还挺简单的;更不用说像vite
这种dev server
还内置了worker-loader
,开箱即用(顺便说一句,vite
也对WASM
提供了开箱即用的加载,心动😁)。
worker
虽好,但是其运行上下文跟主线程还是不一样的,实际上是一个子集语法,因此DOM
对象和很大一部分的BOM
对象都不能访问和使用;具体限制可以参考——Web Workers 可以使用的函数和类 - Web API 接口参考 | MDN。
Worker 的通信
主线程和Worker
实例之间都是通过postMessage
方法来向对方发送消息,而又通过onmessage
方法来注册各自的消息接收处理事件。
如何在 Worker 中实现 excel 的导出
前端有个著名的excel
导出库——sheetjs
,幸运的是这个库的大部分方法都能在worker
中直接进行使用,因此基本上可以参照正常的导出逻辑搬到worker
文件中即可。这里不会探讨sheetjs
的具体使用细节(因为这种API
使用文档网上太多了,就不赘述了),假设此时可以获取到对透视表表格本身的单元格二维数组(即以行为主导,每行里面的元素就是列),那么根据二维数组就能生成一个WorkBook
对象。
而有了WorkBook
对象,正常来说基于XLSX.writeFile()
等方法就能直接进行excel
下载操作了;一般现在的文件下载操作都是通过a
标签来实现的,而worker
本身是限制DOM
访问的,因此不能通过上述方法在worker
内实现下载操作,所以应该将下载操作放在主线程,而将必要的数据传送到主线程。
那么问题来了,应该选择传送什么样的数据到主线程?
思路一:传送 WorkBook 对象
从直觉上来说直接把WorkBook
对象(包括处理完的数据)传送到主线程,数据量如果太大,那么通信成本也很大那么就跟不用webWorker的时间耗费是一样,然后让主线程继续接着XLSX.writeFile()
方法的路子不就好了。但事实证明,这种思路有两个弊端:
- 线程间的通信成本:
WorkBook
对象本质上是一个包含大量数据的对象结构(包含单元格数据和单元格设置等),而线程间的通信成本会随着数据量加大而陡增(结构化克隆,序列化,反序列化等等);当数据量较大时,主线程在接收来自Worker
的消息依然会造成明显的阻塞。 - 从
WorkBook
对象到下载的成本:由于WorkBook
对象本身不能直接进行下载,因此XLSX.writeFile()
这类方法内部都需要先将WorkBook
对象转为Blob/File
对象,然后通过URL.createObjectURL()
来创建一个可访问的Blob URL
;但实际上将WorkBook
对象转为Blob/File
对象也是一个挺耗时的过程。
这里可以看一个具体导出过程的用时统计即可看出上述弊端:
综上,如果采用该方案,那么worker
的使用效果就很尴尬😅了。
思路二:传送 Blob URL
幸运的是,worker
内可以使用URL.createObjectURL()
方法[1],且创建的Blob URL
可以被主线程访问[2]!
到此,剩下的问题就只有一个了——那就是如何将WorkBook
对象转为Blob
对象;事实上,sheetjs
本身就提供了将WorkBook
对象转为二进制数据(Uint8Array
)的API
——XLSX.write(),所以这事就很简单了。
至此,只需要将Blob URL
传送到主线程即可完成最后的下载步骤;显然,传送一个URL
字符串的通信成本几乎为0,这样就可以把excel
导出下载任务的绝大多数运算控制在worker
内部了,最小可能的对主线程进行占用。
gitee仓库:https://gitee.com/jackal123jackal/web-worker.git
参考连接:
- WebWorker 优化数据导出下载 Excel 用户操作体验:https://juejin.cn/post/7128674965821325326#heading-41
- 基于xlsx.js前端导出多sheet的Excel:https://juejin.cn/post/7004097366462578724
- 让你用最简单的方式使用Vue2 + Web Worker + js-xlsx 解析excel数据:https://www.imgeek.org/article/825358544
- Vue前端的 Excel 导入和导出功能:https://www.cnblogs.com/dongh/p/16808586.html
- 基于worker实现excel报表导出下载:https://note.xiexuefeng.cc/post/worker-and-excel-export/#%E5%A6%82%E4%BD%95%E5%9C%A8-worker-%E4%B8%AD%E5%AE%9E%E7%8E%B0-excel-%E7%9A%84%E5%AF%BC%E5%87%BA