前端可以不用依赖后端实现导出大数据了


theme: channing-cyan
hightlight: channing-cyan

前言

在我们公司表格数据导出都是前端去处理。一开始数据量不大,倒没什么问题。但随着数据量的加大,问题也逐渐暴露出来。

一天的数据量有一来万条,导出一定时间范围的数据,30天就得30来万条数据。

那会测试直接给我导出 60 万条数据都存到一个 Excel 表中,页面直接卡死掉,动都动不了,后面直接崩溃掉。

那会为什么导出选择由前端去做呢?

  • 多语言问题:有些内置数据(如:文件分类,计算机组等信息)需要支持多语言,以及表格 header 头。
  • 数据转换问题:有些内置数据返回的是数值类型,需要转成对应的真正的数据。
  • 导出表格字段问题:用户可以通过切换列来控制具体导出哪些字段。

排除原因

经过排查:导出大量数据通常涉及大量的计算、DOM 操作或文件生成等复杂操作,这些操作会在主线程中执行。如果这些操作耗时过长,主线程会被阻塞,导致页面无法响应用户交互(如点击、滚动等),表现为页面卡死

那是否把这些大量的计算、DOM 操作或文件生成等复杂操作,放到子进进程去处理,不就解决了吗?

这就说到了今天的主角:Web Workers

Web Workers 介绍

Web Workers 使得一个Web应用程序可以在与主线程分离的后台线程中运行一个脚本。

这样做的好处在于可以在一个单独的线程中执行费时的处理任务,从而允许主(通常是UI)线程运行而不被阻塞。

它的作用就是给JS创造多线程运行环境,允许主线程创建worker线程,分配任务给后者,主线程运行的同时worker线程也在运行,相互不干扰,在worker线程运行结束后把结果返回给主线程。这样做的好处是主线程可以把计算密集型或高延迟的任务交给worker线程执行,这样主线程就会变得轻松,不会被阻塞或拖慢。这并不意味着JS语言本身支持了多线程能力,而是浏览器作为宿主环境提供了JS一个多线程运行的环境。

不过因为worker一旦新建,就会一直运行,不会被主线程的活动打断,这样有利于随时响应主线程的通性,但是也会造成资源的浪费,所以不应过度使用,用完注意关闭。或者说:如果worker无实例引用,该worker空闲后立即会被关闭;如果worker实列引用不为0,该worker空闲也不会被关闭。

Web Workers 使用

  1. 创建 Worker 对象:通过 new Worker(url) 创建一个 Worker 对象,这里的 url 指向你预先编写的 JavaScript 文件路径,这个文件内包含 Workers 将要执行的脚本内容。
  2. 发送消息:你可以使用 worker.postMessage(message) 方法从主脚本向 Worker 发送数据。
  3. 处理 Worker 发送的消息:在主脚本中,设置 worker.onmessage 事件监听器来处理 Worker 发回来的数据。
  4. 终止 Worker:如果不再需要 Worker,可以调用 worker.terminate() 方法来停止 Worker。
  5. 监听错误:可以通过添加 onerror 事件监听器来处理 Worker 中可能出现的错误。

主线程脚本

  const myWorker = new Worker('worker.js')const nums = [10, 20]myWorker.postMessage(nums)myWorker.onmessage = function(e) {result = e.dataconsole.log('主进程接收子进程传递回来的数据:', e.data)// 停止 Workerworker.terminate()}myWorker.onerror = function(e) {console.log('监听错误')}

Worker 脚本

onmessage = function(e) {var data = e.data;var result = data[0] * data[1];postMessage(result);
}

Web Workers 实战 Excel 导出

基本案例有了,但还是遇到一些坑。下面开始一个个填坑。

问题1:vue 项目如何配置 web worker

这里需要下载第三方 loader, 来编译 workers 脚本。

npm install worker-loader@3.0.8

接下来,修改 vue.config.js 文件:

// vue.config.js
module.exports = {chainWebpack(config) {config.module.rule('worker').test(/\.worker\.js$/).use('worker-loader').loader('worker-loader').options({}).end()  }
}

注意test() 设置了文件名后缀是 .worker.js 则为 worker 脚本文件

到这里第一个问题就解决了。。。


问题2:修改了 web worker 后,重新编译打包没有生效

vue项目一改动到代码文件就会重新编译。

但在调试过程中,修改了 worker 脚本,发现一直没有修复到问题,一开始也是很怀疑自己是不是逻辑出错了。

通过 debug 才发现,代码一直没有修改。

后面每次修改 worker 脚本,都会重新启动 vue 项目,一开始问题是解决了。

但偶尔还是会没有修改到代码。

最终排查到:原来是每次重新编译时,要删除掉 node_modules 目录下的 .cache 文件夹
在这里插入图片描述

才会重新加载新 worker 脚本代码

问题3:主进程向子进程发送参数时,若参数存在对象,会报错

这里主要是生产 csvData 数据(key: value)中的 value 是一个对象结构时,发送给到 子进程,浏览器会报错。

这里解决方法是:将 value 进行序列化处理

// * 判断 csvData 中的值是否存在对象,需要序列化处理
const keys = csvHeader.map(item => item.key)
csvData = csvData.map(row => {return keys.reduce((acc, prev) => {acc[prev] = typeof row[prev] === 'object' ? JSON.stringify(row[prev]) : row[prev]return acc}, {})
})

问题4:在子进程中下载文件失败

主进程去结合实际业务逻辑生成 csvHeadercsvData 数据后,发送给到子进程,由其生成 Excel 文件流,并下载下来。

// 主进程
const { csvHeader, csvData } = generateExcelData(data)// 子进程
import Excel from 'exceljs'
self.onmessage = async function(e) {const { csvData, csvHeader } = e.dataconst workbook = new Excel.Workbook()const worksheet = workbook.addWorksheet('My Sheet')worksheet.columns = csvHeadercsvData.forEach(row => worksheet.addRow(row))// 生成 Excel 文件的 Bufferconst excelBuffer = await workbook.xlsx.writeBuffer()// TODO 下载文件
}

经过调试发现文件下载不下来,查阅资料得出:

主要原因在于 Web Workers 的设计限制。具体来说,Web Workers 没有直接访问浏览器的 DOM 和一些与用户界面交互的功能,包括文件下载

所以这里只能将 Excel 文件的 Buffer转成blog发送给到主进程进行文件下载。
主进程

import { saveAs } from 'file-saver'
import ExportWorker from './export.worker.js'
const worker = new ExportWorker()
worker.postMessage({csvData: csvData,csvHeader: csvHeader
})worker.onmessage = async(e) => {const { chunk: blog } = e.datasaveAs(blog, filename)
}

worker 脚本

import Excel from 'exceljs'self.onmessage = async function(e) {const { csvData, csvHeader } = e.dataconst workbook = new Excel.Workbook()const worksheet = workbook.addWorksheet('My Sheet')worksheet.columns = csvHeadercsvData.forEach(row => worksheet.addRow(row))// 生成 Excel 文件的 Bufferconst excelBuffer = await workbook.xlsx.writeBuffer()const blob = new Blob([excelBuffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })self.postMessage({ chunk: blob })
}

源码

主进程

import { saveAs } from 'file-saver'
import ExportWorker from './export.worker.js'
/**
* 导出数据为 XLSX(通过 web Worker)
* @param {Object} csvHeader XLSX 头
* @param {Array} csvData 数据
* @param {String} filename 文件名
*/
const exportDataToXLSXByWorker = (csvHeader, csvData, filename) => {const worker = new ExportWorker()// * 判断 csvData 中的值是否存在对象,需要序列化处理const keys = csvHeader.map(item => item.key)csvData = csvData.map(row => {return keys.reduce((acc, prev) => {acc[prev] = typeof row[prev] === 'object' ? JSON.stringify(row[prev]) : row[prev]return acc}, {})})worker.postMessage({csvData: csvData,csvHeader: csvHeader})worker.onmessage = async(e) => {const { chunk: blog } = e.datasaveAs(blog, filename)}
}

worker 脚本

import Excel from 'exceljs'self.onmessage = async function(e) {const { csvData, csvHeader } = e.dataconst workbook = new Excel.Workbook()const worksheet = workbook.addWorksheet('My Sheet')worksheet.columns = csvHeadercsvData.forEach(row => worksheet.addRow(row))// 生成 Excel 文件的 Bufferconst excelBuffer = await workbook.xlsx.writeBuffer()const blob = new Blob([excelBuffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })self.postMessage({ chunk: blob })
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/895392.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

游戏引擎学习第99天

仓库:https://gitee.com/mrxiao_com/2d_game_2 黑板:制作一些光场(Light Field) 当前的目标是为游戏添加光照系统,并已完成了法线映射(normal maps)的管道,但还没有创建可以供这些正常映射采样的光场。为了继续推进&…

通过 Docker 安装和部署 KeyDB v6.3.4 的详细步骤

KeyDB 是一种高性能的开源内存数据库,最初是基于 Redis 项目开发的,但在性能、特性和功能上进行了许多增强和改进。它兼容 Redis 的大部分命令和数据结构,因此可以作为 Redis 的替代品使用,尤其是在需要更高性能和多线程支持的场景…

Android Studio 打包App问题

一、场景 windows 电脑C 盘空间越来越少,所有软件默认位置都往C盘用户目录写入数据,于是开始准备整理,Android Studio 相关的 .android 和 .gradle 目录成为了目标。 二、问题出现 1、将C盘的.gradle 目录拷贝到D盘,文件比较大&a…

鸿蒙HarmonyOS NEXT开发:横竖屏切换开发实践

文章目录 一、概述二、窗口旋转说明1、配置module.json5的orientation字段2、调用窗口的setPreferredOrientation方法 四、性能优化1、使用自定义组件冻结2、对图片使用autoResize3、排查一些耗时操作 四、常见场景示例1、视频类应用横竖屏开发2、游戏类应用横屏开发 五、其他常…

linux安装jdk 许可证确认 user did not accept the oracle-license-v1-1 license

一定要接受许可证,不然会出现 一、添加 ppa第三方软件源 sudo add-apt-repository ppa:ts.sch.gr/ppa二、更新系统软件包列表 sudo apt-get update三、接受许可证 echo debconf shared/accepted-oracle-license-v1-1 select true | sudo debconf-set-selection…

DeepSeek 助力 Vue 开发:打造丝滑的进度条

前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 Deep…

2025年SEO自动优化工具

随着2025年互联网的快速发展,越来越多的企业和个人意识到,拥有一个排名靠前的网站对于吸引客户、增加流量、提高转化率至关重要。而要想让自己的网站脱颖而出,获得更多曝光,最重要的一项工作就是进行SEO优化。传统的SEO优化方式通…

华硕笔记本怎么一键恢复出厂系统_华硕笔记本一键恢复出厂系统教程

华硕笔记本怎么一键恢复出厂系统? 华硕一键恢复出厂系统是一个安全、高效、方便的恢复方式,让您轻松还原出厂设置,以获得更好的系统性能。如果您的华硕电脑遇到问题,可以使用华硕一键恢复出厂系统功能。下面小编就教大家华硕笔记本…

Unity 编辑器热更C# FastScriptReload

工具源码:https://github.com/handzlikchris/FastScriptReload 介绍 用于运行时修改C#后能快速重新编译C#并生效,避免每次改C#,unity全部代码重新编译,耗时旧且需要重启游戏。 使用 需要手动调整AssetPipeline自动刷新模式&…

vue纯静态实现 视频转GIF 功能(附源码)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、实现后的效果二、使用步骤1.引入库2.下载or复制出来js3. 前端实现 总结 前言 一天一个小demo 今天来一个vue纯静态实现 视频转GIF 功能 上一篇我们讲到了…

因果机器学习(CausalML)前沿创新思路

结合了传统因果推断与机器学习的因果机器学习是目前AI领域的前沿研究方向,其核心优势在于将因果逻辑融入数据驱动模型,从根本上解决了传统方法的缺陷。因此,它也是突破传统机器学习瓶颈的关键方向,不仅当下热度高,在未…

【CubeMX+STM32】SD卡 U盘文件系统 USB+FATFS

本篇,将使用CubeMXKeil, 创建一个 USBTF卡存储FatFS 的虚拟U盘读写工程。 目录 一、简述 二、CubeMX 配置 SDIO DMA FatFs USB 三、Keil 编辑代码 四、实验效果 串口助手,实现效果: U盘,识别效果: 一、简述 上…

docker nginx 配置文件详解

在平常的开发工作中,我们经常需要访问静态资源(图片、HTML页面等)、访问文件目录、部署项目时进行负载均衡等。那么我们就会使用到Nginx,nginx.conf 的配置至关重要。那么今天主要结合访问静态资源、负载均衡等总结下 nginx.conf …

解读 Flink Source 接口重构后的 KafkaSource

前言 Apache Kafka 和 Apache Flink 的结合,为构建实时流处理应用提供了一套强大的解决方案[1]。Kafka 作为高吞吐量、低延迟的分布式消息队列,负责数据的采集、缓冲和分发;而 Flink 则是功能强大的流处理引擎,负责对数据进行实时…

【推理llm论文精读】DeepSeek V3技术论文_精工见效果

先附上原始论文和效果对比https://arxiv.org/pdf/2412.19437 摘要 (Abstract) DeepSeek-V3是DeepSeek-AI团队推出的最新力作,一个强大的混合专家(Mixture-of-Experts,MoE)语言模型。它拥有671B的总参数量,但每个tok…

如何使用Java语言在Idea和Android中分别建立服务端和客户端实现局域网聊天

手把手教你用Java语言在Idea和Android中分别建立服务端和客户端实现局域网聊天 目录 文章目录 手把手教你用**Java**语言在**Idea**和**Android**中分别建立**服务端**和**客户端**实现局域网聊天**目录**[toc]**基本实现****问题分析****服务端**Idea:结构预览Server类代码解…

java韩顺平最新教程,Java工程师进阶

简介 HikariCP 是用于创建和管理连接,利用“池”的方式复用连接减少资源开销,和其他数据源一样,也具有连接数控制、连接可靠性测试、连接泄露控制、缓存语句等功能,另外,和 druid 一样,HikariCP 也支持监控…

如何在 IDE 里使用 DeepSeek?

近期,阿里云百炼平台重磅推出 DeepSeek-V3、DeepSeek-R1、DeepSeek-R1-Distill-Qwen-32B 等 6 款模型,进一步丰富其 AI 模型矩阵。与此同时,通义灵码也紧跟步伐,全新上线模型选择功能,支持基于百炼的 DeepSeek-V3 和 D…

网络安全技术复习总结

1|0第一章 概论 1.网络安全发展阶段包括四个阶段:通信安全、计算机安全、网络安全、网络空间安全。 2.2017年6月1日,我国第一部全面规范网络空间安全的基础性法律《中华人民共和国网络安全法》正式实施。 3.2021年 6月10日,《中华人民共和…

DedeBIZ系统审计小结

之前简单审计过DedeBIZ系统,网上还没有对这个系统的漏洞有过详尽的分析,于是重新审计并总结文章,记录下自己审计的过程。 https://github.com/DedeBIZ/DedeV6/archive/refs/tags/6.2.10.zip 📌DedeBIZ 系统并非基于 MVC 框架&…