Vue 3 前端导出 Excel:xlsx + file-saver 封装 useExportExcel

安装

npm install xlsx file-saver

基础用法

import * as XLSX from 'xlsx'
import { saveAs } from 'file-saver'

const data = [
    { name: '张三', age: 18, city: '北京' },
    { name: '李四', age: 20, city: '上海' }
]

const ws = XLSX.utils.json_to_sheet(data)
const wb = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1')

const buf = XLSX.write(wb, { bookType: 'xlsx', type: 'array' })
const blob = new Blob([buf], {
    type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
})

saveAs(blob, '数据.xlsx')

封装 useExportExcel

// useExportExcel.js
import * as XLSX from 'xlsx'
import { saveAs } from 'file-saver'
import { ref } from 'vue'

export function useExportExcel() {
    const exporting = ref(false)

    const exportExcel = (data = [], fileName = '数据表', headerMap = {}) => {
        if (!data.length) return

        exporting.value = true
        try {
            // 按 headerMap 重命名列
            const exportData = data.map(row => {
                const newRow = {}
                Object.keys(row).forEach(key => {
                    newRow[headerMap[key] || key] = row[key]
                })
                return newRow
            })

            const ws = XLSX.utils.json_to_sheet(exportData)
            const wb = XLSX.utils.book_new()
            XLSX.utils.book_append_sheet(wb, ws, 'Sheet1')

            const buf = XLSX.write(wb, { bookType: 'xlsx', type: 'array' })
            const blob = new Blob([buf], {
                type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
            })

            saveAs(blob, `${fileName}.xlsx`)
        } finally {
            exporting.value = false
        }
    }

    const exportSelected = (allData, selectedRows, fileName, headerMap = {}) => {
        exportExcel(selectedRows?.length ? selectedRows : allData, fileName, headerMap)
    }

    return { exporting, exportExcel, exportSelected }
}

配合 Element Plus el-table 使用

<template>
    <el-button :loading="exporting" @click="onExportAll">导出全部</el-button>
    <el-button :loading="exporting" @click="onExportSelected">导出选中</el-button>

    <el-table :data="tableData" @selection-change="selectedRows = $event">
        <el-table-column type="selection" />
        <el-table-column prop="name" label="姓名" />
        <el-table-column prop="age" label="年龄" />
        <el-table-column prop="city" label="城市" />
    </el-table>
</template>

<script setup>
import { ref } from 'vue'
import { useExportExcel } from './useExportExcel'

const tableData = ref([
    { name: '张三', age: 18, city: '北京' },
    { name: '李四', age: 20, city: '上海' }
])

const selectedRows = ref([])

const { exporting, exportExcel, exportSelected } = useExportExcel()

const headerMap = { name: '姓名', age: '年龄', city: '城市' }

const onExportAll = () => exportExcel(tableData.value, '用户列表', headerMap)
const onExportSelected = () => exportSelected(tableData.value, selectedRows.value, '选中用户', headerMap)
</script>

导出 DOM table

如果页面已有 <table> 元素,不需要操作数据,直接从 DOM 生成:

const table = document.getElementById('myTable')
const wb = XLSX.utils.table_to_book(table)
XLSX.writeFile(wb, 'table.xlsx')

大数据后端流式下载

超过 5 万行时前端导出会明显卡顿(浏览器内存限制),推荐后端生成流式 Excel:

const exportExcelFromServer = async (params) => {
    const res = await axios({
        url: '/api/export',
        method: 'get',
        params,
        responseType: 'blob'
    })

    const blob = new Blob([res.data])
    const link = document.createElement('a')
    link.href = URL.createObjectURL(blob)
    link.download = '数据.xlsx'
    link.click()
    URL.revokeObjectURL(link.href)
}

后端(Laravel + Maatwebsite\Excel):

return Excel::download(new UserExport(), 'users.xlsx');