在Vue 3项目中,当业务需要导出带边框、背景色、合并单元格的Excel报表时,使用sheetjs社区版往往无法满足样式需求,而ExcelJS则是一个功能强大的Node.js和浏览器端Excel处理库,能够精细控制单元格样式、合并区域、列宽行高等。本文将围绕Vue 3环境,从零开始演示如何利用ExcelJS生成带样式的复杂Excel报表。

安装与基础用法
首先需要在Vue 3项目中安装ExcelJS依赖。打开终端执行以下命令:
npm install exceljs
安装完成后,可以在组件中导入ExcelJS并创建Workbook对象。Workbook是Excel文档的顶层容器,一个Workbook可以包含多个Worksheet。下面的代码演示了在Vue 3的setup语法中创建一个基础的工作簿,并填充两行数据:
import ExcelJS from 'exceljs';
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('销售报表');
worksheet.addRow(['商品名称', '销量', '单价']);
worksheet.addRow(['苹果', 120, 5.5]);
worksheet.addRow(['香蕉', 80, 3.2]);
// 导出文件
const buffer = await workbook.xlsx.writeBuffer();
const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = '报表.xlsx';
link.click();
URL.revokeObjectURL(link.href);
这段代码创建了一个包含工作表“销售报表”的Excel文件,并添加了三行数据。addRow方法接收数组,会自动按顺序填入A、B、C列。最后将工作簿写入ArrayBuffer,再转换为Blob对象触发下载。注意在Vue 3中,此逻辑通常放在按钮点击事件里,需要处理异步操作,可以配合async/await。
ExcelJS在浏览器端同样支持流式写入到文件,但生成完整buffer的方式更适合中小规模数据。对于大型报表,可以考虑使用exceljs的stream模式,这会在性能章节进一步讨论。
单元格样式定制详解
ExcelJS最突出的能力是单元格级别的样式控制。每个单元格可以通过font、fill、border、alignment等属性单独设置字体、背景色、边框和对齐方式。以下代码演示了为标题行添加加粗字体、深色背景和居中样式:
const headerRow = worksheet.getRow(1);
headerRow.font = { name: '微软雅黑', size: 12, bold: true, color: { argb: 'FFFFFFFF' } };
headerRow.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF4472C4' } };
headerRow.alignment = { vertical: 'middle', horizontal: 'center' };
headerRow.eachCell((cell) => {
cell.border = {
top: { style: 'thin', color: { argb: 'FF000000' } },
left: { style: 'thin', color: { argb: 'FF000000' } },
bottom: { style: 'thin', color: { argb: 'FF000000' } },
right: { style: 'thin', color: { argb: 'FF000000' } }
};
});
字体对象中的argb颜色格式为AARRGGBB,其中AA代表透明度,FF表示完全不透明。填充类型pattern: 'solid'表示纯色填充,配合fgColor指定前景色。边框的style支持thin、medium、thick、dashed等多种预设值,可以分别设置四个方向。这里对整行进行了样式设置,同时通过eachCell方法为每个单元格添加边框,确保视觉完整。
除了整行设置,也可以直接获取某个单元格精确控制。例如给销量列设置数字格式为带千位分隔符,并右对齐:
worksheet.getCell('B2').numFmt = '#,##0';
worksheet.getCell('B2').alignment = { horizontal: 'right' };
单元格引用可以使用A1格式,也可以通过行列编号访问,例如worksheet.getRow(2).getCell(2)。ExcelJS对样式属性未设置时采用默认值,所以可以按需部分设置,未指定的方向边框保持默认样式。
复杂报表:合并单元格与多级表头
真实业务中的报表常常需要多层表头,例如第一行跨多列显示大类“华东区”,下方再细分“一月”“二月”。ExcelJS通过mergeCells方法实现单元格合并,多级表头本质上就是按行合并和按列合并的组合。以下示例构建了一个带两级表头的销售报表:
// 添加第一行作为大标题,合并A1:F1
worksheet.mergeCells('A1:F1');
worksheet.getCell('A1').value = '2024年销售数据汇总';
worksheet.getCell('A1').font = { size: 14, bold: true };
worksheet.getCell('A1').alignment = { horizontal: 'center', vertical: 'middle' };
// 第二行:两个大区标题
worksheet.getCell('A2').value = '华东区';
worksheet.getCell('D2').value = '华南区';
worksheet.mergeCells('A2:C2');
worksheet.mergeCells('D2:F2');
// 第三行:月份细分
worksheet.addRow(['一月', '二月', '三月', '一月', '二月', '三月']);
// 调整表头样式...
上面的代码先在A1:F1区域合并了一个大标题,然后在第二行分别将A2:C2和D2:F2合并为“华东区”和“华南区”,第三行则是对应的三个月份。此时表格已经具备了清晰的层级结构。需要注意的是,合并后除了左上角单元格外,其余单元格的值会被忽略,因此写入数据时应该只操作合并区域的起始单元格,否则可能造成数据丢失或样式不一致。
列宽与行高同样重要,ExcelJS通过worksheet.columns和row.height来调整。列宽可以针对每一列单独设置,也可以使用数组统一处理:
worksheet.columns = [
{ key: 'product', width: 20 },
{ key: 'region', width: 15 },
{ key: 'sales', width: 12 }
];
// 设置第一行行高
worksheet.getRow(1).height = 30;
worksheet.getRow(2).height = 25;
worksheet.getRow(3).height = 20;
这里的key参数在配合worksheet.addRows传入对象数组时非常有用,可以自动对应列,同时也便于后续读取单元格值。如果使用columns定义,ExcelJS会自动生成表头,但需要手动处理样式。
Vue 3整合与性能优化
在实际Vue 3应用中,推荐将导出逻辑封装成独立的工具函数,以便在多个组件中复用。一个常见的封装如下:
// utils/exportExcel.js
import ExcelJS from 'exceljs';
export async function exportStyledExcel(fileName, sheetName, buildSheet) {
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet(sheetName);
buildSheet(worksheet);
const buffer = await workbook.xlsx.writeBuffer();
const blob = new Blob([buffer], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName.endsWith('.xlsx') ? fileName : `${fileName}.xlsx`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
组件中调用时传入一个回调函数,在回调内完成样式设置和数据填充。这样做既保持了组件的整洁,也使得导出逻辑可测试、可维护。对于数据量较大的表格(例如上万行),一次性写入所有数据可能导致浏览器卡顿。ExcelJS提供了流式写入API,可以逐行添加而不是一次性构建整个数组:
const workbook = new ExcelJS.stream.xlsx.WorkbookWriter({ filename: 'large.xlsx' });
const worksheet = workbook.addWorksheet('大数据');
// 逐行写入数据
for (let i = 0; i < 50000; i++) {
worksheet.addRow([i, `名称${i}`, Math.random() * 1000]).commit();
}
await worksheet.commit();
await workbook.commit();
流式模式在Node.js服务端使用效果最佳,浏览器端虽然也支持,但会受到内存和文件系统API限制。对于大多数前端导出场景,数据量在几千行以内时,使用常规的writeBuffer方式足够流畅。如果数据量接近数万行,建议考虑将导出任务交给后端处理,或者使用Web Worker避免阻塞主线程。
另外,在Vue 3中需要注意异步导出期间用户可能多次点击按钮,可以增加一个loading状态防止重复触发。同时,生成的Excel文件默认包含的样式在Excel和WPS中表现基本一致,但个别字体或高级填充效果可能存在兼容性差异,测试时应覆盖目标用户常用的办公软件。
通过上述方法,Vue 3开发者可以充分利用ExcelJS的强大样式能力,为业务系统输出专业、规范的Excel报表,显著提升数据导出功能的实用性和用户体验。