今天聊一下前端万级数据量表格预览及前端导出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 // 样式是否也按需加载

            }

          ]

        ]

      }

在这里插入图片描述
  1. 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>

正文完