前端可以不用依賴后端實現導出大數據了


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.pswp.cn/news/895392.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/895392.shtml
英文地址,請注明出處:http://en.pswp.cn/news/895392.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java 后端開發:如何提取請求頭中的 Cookie 和身份驗證 Token

在 Java 后端開發中,處理 HTTP 請求時,經常需要從請求頭中提取特定的信息,如 Cookie 和身份驗證 Token。這些信息對于實現用戶會話管理、身份驗證和授權等功能至關重要。本文將詳細介紹如何在 Java 后端接口中提取請求頭中的 Cookie 和 Token…

游戲引擎學習第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…

[NGINX]nginx-rtmp-module相關配置

1.核心配置 rtmp 保存所有RTMP設置的塊 server 聲明RTMP服務實例,語法server { ... } rtmp {server {} }listen 為NGINX添加監聽套接字以接受RTMP連接。語法: listen (addr[:port]|port|unix:path) [bind] [ipv6onlyon|off][so_keepaliveon|off|keepidle:keepintv…

DeepSeek 助力 Vue 開發:打造絲滑的進度條

前言:哈嘍,大家好,今天給大家分享一篇文章!并提供具體代碼幫助大家深入理解,徹底掌握!創作不易,如果能幫助到大家或者給大家一些靈感和啟發,歡迎收藏關注哦 💕 目錄 Deep…

2025年SEO自動優化工具

隨著2025年互聯網的快速發展,越來越多的企業和個人意識到,擁有一個排名靠前的網站對于吸引客戶、增加流量、提高轉化率至關重要。而要想讓自己的網站脫穎而出,獲得更多曝光,最重要的一項工作就是進行SEO優化。傳統的SEO優化方式通…

element-ui時間組件同一個月內選擇/30天內選擇

element-ui時間組件同一個月內選擇/30天內選擇 同一個月 <el-date-picker v-model"time" type"datetimerange"range-separator"至" start-placeholder"開始時間"value-format"timestamp" :picker-options"pickerO…

華碩筆記本怎么一鍵恢復出廠系統_華碩筆記本一鍵恢復出廠系統教程

華碩筆記本怎么一鍵恢復出廠系統&#xff1f; 華碩一鍵恢復出廠系統是一個安全、高效、方便的恢復方式&#xff0c;讓您輕松還原出廠設置&#xff0c;以獲得更好的系統性能。如果您的華碩電腦遇到問題&#xff0c;可以使用華碩一鍵恢復出廠系統功能。下面小編就教大家華碩筆記本…

SQL CASE表達式的用法

SQL CASE表達式的用法 一、CASE表達式的基礎語法簡單CASE表達式搜索CASE表達式 二、簡單CASE表達式的應用示例三、搜索CASE表達式的應用示例四、CASE表達式在聚合函數中的應用五、嵌套CASE表達式的應用 今天在也無力用到了CASE表達式&#xff0c;于是有了這篇博客&#xff0c;C…

Unity 編輯器熱更C# FastScriptReload

工具源碼&#xff1a;https://github.com/handzlikchris/FastScriptReload 介紹 用于運行時修改C#后能快速重新編譯C#并生效&#xff0c;避免每次改C#&#xff0c;unity全部代碼重新編譯&#xff0c;耗時舊且需要重啟游戲。 使用 需要手動調整AssetPipeline自動刷新模式&…

MySQL InnoDB引擎 MVCC

MVCC&#xff08;Multi-Version Concurrency Control&#xff09;即多版本并發控制&#xff0c;是 MySQL 的 InnoDB 存儲引擎實現并發控制的一種重要技術。它在很多情況下避免了加鎖操作&#xff0c;從而提高了數據庫的并發性能。 一、原理 MVCC 的核心思想是通過保存數據在某…

從0到1:ArkTS實現鴻蒙策略模式全解析

策略模式初窺 策略模式&#xff08;Strategy Pattern&#xff09;是一種行為型設計模式&#xff0c;它定義了一系列算法&#xff0c;并將每個算法封裝起來&#xff0c;使它們可以相互替換&#xff0c;且算法的變化不會影響到使用算法的客戶端。在軟件開發中&#xff0c;策略模…

iOS AES/CBC/CTR加解密以及AES-CMAC

感覺iOS自帶的CryptoKit不好用&#xff0c;有個第三方庫CryptoSwift還不錯&#xff0c;好巧不巧&#xff0c;清理過Xcode緩存后死活下載不下來&#xff0c;當然也可以自己編譯個Framework&#xff0c;但是偏偏不想用第三方庫了&#xff0c;于是研究了一下&#xff0c;自帶的Com…

vue純靜態實現 視頻轉GIF 功能(附源碼)

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、實現后的效果二、使用步驟1.引入庫2.下載or復制出來js3. 前端實現 總結 前言 一天一個小demo 今天來一個vue純靜態實現 視頻轉GIF 功能 上一篇我們講到了…

因果機器學習(CausalML)前沿創新思路

結合了傳統因果推斷與機器學習的因果機器學習是目前AI領域的前沿研究方向&#xff0c;其核心優勢在于將因果邏輯融入數據驅動模型&#xff0c;從根本上解決了傳統方法的缺陷。因此&#xff0c;它也是突破傳統機器學習瓶頸的關鍵方向&#xff0c;不僅當下熱度高&#xff0c;在未…

【CubeMX+STM32】SD卡 U盤文件系統 USB+FATFS

本篇&#xff0c;將使用CubeMXKeil, 創建一個 USBTF卡存儲FatFS 的虛擬U盤讀寫工程。 目錄 一、簡述 二、CubeMX 配置 SDIO DMA FatFs USB 三、Keil 編輯代碼 四、實驗效果 串口助手&#xff0c;實現效果&#xff1a; U盤&#xff0c;識別效果&#xff1a; 一、簡述 上…

docker nginx 配置文件詳解

在平常的開發工作中&#xff0c;我們經常需要訪問靜態資源&#xff08;圖片、HTML頁面等&#xff09;、訪問文件目錄、部署項目時進行負載均衡等。那么我們就會使用到Nginx&#xff0c;nginx.conf 的配置至關重要。那么今天主要結合訪問靜態資源、負載均衡等總結下 nginx.conf …