前端万级数据量表格预览及前端导出Excel方案、组件封装(基于vxetable虚拟滚动和web worker)

如题,本文主要解决前端展示渲染大数据量table数据及导出Excel的问题,主要是通过使用v3版本的vxetable和webworker来实现的。亲测可以表格可渲染30w条数据,可在10s内导出30w条数据Excel。

框架:vue2;
在这里插入图片描述

一、安装vxetable

可参考官网:https://vxetable.cn/v3/#/table/start/use

  1. 安装按需加载的插件
npm install babel-plugin-import -D
  1. 修改文件 .babelrc 或 babel.config.js
{
        "plugins": [
          [
            "import",
            {
              "libraryName": "vxe-table",
              "style": true // 样式是否也按需加载
            }
          ]
        ]
      }

在这里插入图片描述
3. main.js中全局按需引入模块

import { VXETable, Icon, Column, Table } from 'vxe-table'
Vue.use(Icon).use(Column).use(Table).use(VXETable)

这样VXETable就已经在项目中安装注册完成,接下来进行安装vue-worker

二、安装vue-worker
  1. 首先安装vue-worker插件;
npm install vue-worker -S
  1. 在main.js中引入
import VueWorker from 'vue-worker'
Vue.use(VueWorker)
  1. 引入导出XLSX的包
    这里有两种方式
    第一种方式:在web worker中使用XLSX的外部CDN链接
importScripts('https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.15.3/xlsx.core.min.js')

然后就可以在web worker中全局使用XLSX了

第二张方式:将上面👆包的js链接下载到本地,放到项目的public文件夹下,可以再建一个workers文件夹存放,如下
在这里插入图片描述
然后再web worker中使用

//importScripts('https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.15.3/xlsx.core.min.js')
importScripts(location.origin + '/workers/xlsx.core.min.js')
//因为woker中是有loaction变量的,所以可以通过服务器的绝对路径进行访问,以获得XLSX。
const ws = XLSX.utils.aoa_to_sheet(data)
三、封装相应的table组件

下面是我封装的组件,如果是使用elementUI可以直接引入,若没有使用可以把elementUi相关标签删除替换即可。
vxe-table的Api文档:https://vxetable.cn/v3/#/table/api

组件传参说明:仅有两个参数

rowList行记录数据
colList列记录数据
<result-table :rowList="rowList" :colList="colList"> </result-table>
<template>
  <div style="height: 100%">
    <div style="display: flex; justify-content: flex-end">
      <el-pagination
        background
        layout="sizes,prev, pager, next,total"
        :page-sizes="[10, 50, 100, 500, 1000]"
        :current-page="currentPageNum"
        :page-size="currentPageSize"
        :total="rowListTotal"
        @current-change="currentChange"
        @size-change="handleSizeChange"
      >
      </el-pagination>
      <el-button
        type="primary"
        @click="handleLargeDataExport"
        size="small"
        icon="el-icon-download"
        style="z-index: 99; margin-right: 10px"
        >导出全部</el-button
      >
    </div>
    <div style="height: calc(100% - 80px); padding: 5px 10px">
      <vxe-table
        border
        ref="xTable1"
        height="100%"
        empty-text="暂无数据"
        show-overflow="title"
        style="overflow: visible"
        show-header-overflow
        :row-config="{ isHover: true }"
        :tooltip-config="{ showAll: true }"
      >
        <vxe-column
          v-for="item in colList"
          :field="item.name"
          :resizable="true"
          min-width="120"
          :title="item.name"
          :key="item.name"
        ></vxe-column>
      </vxe-table>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      currentList: [], //当前页数据
      currentPageNum: 1, //当前页码
      currentPageSize: 50, //当前页尺寸
      rowListTotal: 0 //结果总条数
    }
  },
  props: {
    //行数据
    rowList: {
      type: Array,
      default: []
    },
    // 列数据
    colList: {
      type: Array,
      default: []
    }
  },
  created() {},
  watch: {
    rowList: {
      handler(newVal, oldVal) {
        console.log('rowListHandle')
        this.rowListTotal = newVal.length
        this.currentList = newVal.slice(0, this.currentPageSize)
        this.currentPageNum = 1
      },
      immediate: true
    },
    currentList: {
      handler(newVal, oldVal) {
        console.log('currentListHandle')
        this.$refs.xTable1.loadData(newVal)
      }
    }
  },
  methods: {
    //当前页尺寸改变
    handleSizeChange(pageSize) {
      this.currentPageSize = pageSize
      this.currentList = this.rowList.slice(
        (this.currentPageNum - 1) * this.currentPageSize,
        this.currentPageNum * this.currentPageSize
      )
    },
    // 当前页码改变
    currentChange(pageNum) {
      this.currentPageNum = pageNum
      this.currentList = this.rowList.slice(
        (this.currentPageNum - 1) * this.currentPageSize,
        this.currentPageNum * this.currentPageSize
      )
    },
    // 全部结果导出
    handleLargeDataExport() {
      let handleData = this.rowList
      if (handleData.length == 0) {
        return this.$message('暂无数据!')
      }
      this.$message('正在导出,请稍后...')
      this.$worker
        .run(
          (handleData) => {
            // console.time('handelExcel')
            let data = handleData.map((item, index) => {
              let tempArr = []
              for (let itemKey in item) {
                tempArr.push(item[itemKey])
              }
              return tempArr
            })
            let keyArr = []
            for (let itemKey in handleData[0]) {
              keyArr.push(itemKey)
            }
            data.unshift(keyArr)

            importScripts(location.origin + '/workers/xlsx.core.min.js')
            // importScripts('https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.15.3/xlsx.core.min.js')
            const ws = XLSX.utils.aoa_to_sheet(data)
            const wb = XLSX.utils.book_new()
            XLSX.utils.book_append_sheet(wb, ws, 'data')
            const buf = XLSX.write(wb, { type: 'array', bookType: 'xlsx' })
            // console.timeEnd('handelExcel')
            return buf
          },
          [handleData]
        )
        .then((res) => {
          this.$message({
            message: '已生成文件!',
            type: 'success',
            duration: 2000
          })
          const url = window.URL.createObjectURL(new Blob([res]))
          const link = document.createElement('a')
          link.href = url
          link.download = `全部结果_${new Date().getTime()}.xlsx`
          link.click()
        })
        .catch((err) => {
          console.log(err)
        })
    }
  }
}
</script>

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值