使用web Worker实现前端导出大数据excel表格

需求背景:业务后台下载数据表格是调用后端接口直接下载的,但是数据量超过十万级别后,接口总是出现崩溃的情况,后端经过排查并没有找到原因,所以我就给出前端下载的方案

技术方案:由前端分页查询接口,最后汇总数据下载excel,同时由于数据量大,下载过程会比较漫长,下载过程中不能影响用户进行别的操作,所以使用 Web Worker 开启一个独立的线程进行数据的获取

我是在vue项目中调用的,可根据自己项目技术栈进行修改,好了着急改bug,我直接上代码:

首先,确定一下调用方式

js 复制代码
// 这是需要下载excel的页面.vue

// 定义表头信息
const HEAD_INFO = {
  mid: '客户ID',
  nickName: '昵称',
  livePeriod: '直播期数'
  ...
}

// 点击按钮,调用exportExcel
async exportExcel () {
  this.exportPageLoading = true
  let flowPageSize = 200

  await requestExport({
    extraParams: { ...this.$route.query, page_size: flowPageSize },
    tableHead: HEAD_INFO,
    filename: '支付流水明细',
    typeConversions: [ // 设置单元格类型
      {
        columnCode: 'N', // 第 N 列
        columnType: 'n', // 设置为 数值 类型
        headLevel: 1 // N1 是表头,不设置
      }
    ]
  })
  
  this.exportPageLoading = false
  this.$message({
    type: 'success',
    message: '***数据已成功导出,请注意查收***',
    duration: 6000
  })
},

下面就来实现requestExport方法

js 复制代码
/**
 * 下载excel
 */
export const requestExport = async ({
  extraParams,
  tableHead,
  filename,
  typeConversions
}) => {
  return new Promise((resolve, reject) => {
    let total = 0
    
    // 关于 downloadWorker 文件的路径问题,后面会说
    const worker = new Worker('./downloadWorker.js')

    let list = []
    list[0] = Object.values(tableHead)

    // 启动 worker 请求数据
    worker.postMessage({
      extraParams,
      maxLength,
      tableHead,
      cookie: document.cookie, // 接口需要鉴权,根据自己业务鉴权方式修改
    })

    worker.onmessage = (e) => {
      console.log('Web Worker 返回的数据', e.data)
      list = e.data.list
      total = e.data.total

      // 导出excel
      exportExcel(list, filename, typeConversions)

      resolve()
    }
  })
}

worker 脚本,downloadWorker.js,该文件必须放在网站根目录

js 复制代码
onmessage = async (e) => {
  console.log('web work开始执行了', e.data)

  const { extraParams, maxLength, tableHead, cookie } = e.data
  let total = 0
  let page = 1
  const pageSize = 200
  let isLastPage = false
  let list = []
  list[0] = Object.values(tableHead)

  do {
    try {
      const params = {
        page_size: pageSize,
        ...extraParams,
        page
      }

      let url = 'http://****?'

      for (const key in params) {
        if (key && params[key]) {
          if (url[url.length - 1] === '?') {
            url += `${key}=${params[key]}`
          } else {
            url += `&${key}=${params[key]}`
          }
        }
      }

      const reqHeaders = {
        Cookie: cookie
      }

      const res = await fetch(url, { headers: reqHeaders }).then(function (response) {
        return response.json()
      })

      const { data } = res

      total = data.total

      // 处理数据
      const newList = data.list.map((row) => {
        const keys = Object.keys(tableHead)
        const ret = []
        keys.forEach((key) => {
          ret.push(row[key])
        })
        return ret
      })

      list = list.concat(newList)
      page++

      if (list.length + pageSize >= total) {
        isLastPage = true
      }
    } catch (error) {
      console.log('error', error)
    }
  } while (list.length < total)

  postMessage({ list, total })
}

ok,现在功能已经实现了

注意

new Worker('./downloadWorker.js') 中的 downloadWorker.js 需要是一个网络地址,或者放在当前网站的根目录

不过public目录下的文件不会编译,如果害怕源码泄露可以在打包时编译下再copy到dist目录

相关推荐
We་ct10 分钟前
LeetCode 49. 字母异位词分组:经典哈希解法解析+易错点规避
前端·算法·leetcode·typescript·哈希算法
CHU72903512 分钟前
废品回收小程序前端功能设计逻辑与实践
前端·小程序
lzhdim12 分钟前
微星首款全白设计的M-ATX小板! MPG B850M EDGE TIMAX WIF刀锋 钛评测:性能媲美顶级X870E主板
前端·edge
恋猫de小郭17 分钟前
小米 HyperOS 4 大变样?核心应用以 Rust / Flutter 重写,不兼容老系统
android·前端·人工智能·flutter·ios
李火火的安全圈23 分钟前
基于Yakit、Wavely实现CVE-2025-55182(React Server Components(RSC)) 反序列化漏洞挖掘和POC编写
前端·react.js
Orange_sparkle33 分钟前
dify的web页面如何传入user用户信息进行对话,而不是uuid
前端·人工智能
Amumu1213842 分钟前
Vue Router 和 常用组件库
前端·javascript·vue.js
霍理迪1 小时前
CSS移动端开发及less使用方法
前端·css
2601_949857431 小时前
Flutter for OpenHarmony Web开发助手App实战:HTML参考
前端·flutter·html
爱内卷的学霸一枚1 小时前
现代前端工程化实践:从Vue到React的架构演进与性能优化(7000字深度解析)
前端·vue.js·react.js