后端返回数据让前端做导出

业务背景


 

如果后端只返回数据让前端做导出,那么数据量多会导致页面卡到假死状态,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()方法的路子不就好了。但事实证明,这种思路有两个弊端:

  1. 线程间的通信成本:WorkBook对象本质上是一个包含大量数据的对象结构(包含单元格数据和单元格设置等),而线程间的通信成本会随着数据量加大而陡增(结构化克隆,序列化,反序列化等等);当数据量较大时,主线程在接收来自Worker的消息依然会造成明显的阻塞。
  2. 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

 

posted @ 2022-12-04 01:21  强者之途  阅读(477)  评论(0编辑  收藏  举报
/* 看板娘 */