在Web应用里,报表和数据列表经常需要提供导出功能。传统做法是后端生成Excel文件再返回下载链接,但很多场景下数据已经在浏览器端,直接在前端完成导出能减少一次网络请求,也能让交互更流畅。HTML5提供了Blob、FileReader、URL.createObjectURL等API,配合a标签的download属性,完全可以在不依赖服务器的情况下生成可下载的文件。下面围绕这些能力,把常用的导出方案拆开讲清楚。

一、Blob与下载链:纯前端导出的基础
纯前端能触发文件下载,核心原理是浏览器允许将内存中的二进制或文本数据封装成Blob对象,再用URL.createObjectURL生成一个blob:开头的临时地址。把这个地址赋给a标签的href,同时设置download属性为期望的文件名,模拟点击a标签,浏览器就会执行下载操作。相比早期使用data:URI的做法,Blob更节省内存,对大数据量也更友好,而且不会阻塞页面渲染。下面的基础函数可以复用在多种导出场景中。
function downloadFile(content, filename, mimeType) {
const blob = new Blob([content], { type: mimeType });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
用这个模板可以快速实现CSV导出。CSV是Excel能直接打开的文本格式,实现简单,适合只有纯数据、不需要格式的场景。只要把二维数组的第一行作为表头,后续行按逗号拼接,就能生成一个可下载的CSV文件。需要注意的是,如果字段内容本身包含逗号、换行符或双引号,需要用双引号包裹字段并对内部双引号做转义,否则Excel解析时会出现列错位。
const rows = [
['姓名', '年龄', '城市'],
['张三', 28, '北京'],
['李四', 32, '上海']
];
const csv = rows.map(row => row.join(',')).join('\n');
downloadFile(csv, '用户列表.csv', 'text/csv;charset=utf-8');
CSV虽然简单,但缺点也很明显:中文在部分Excel版本中打开会乱码,不能保留单元格格式、公式、多个工作表,也不能设置列宽。要生成看起来更像样的Excel文件,可以把HTML表格伪装成.xls文件。Excel能够识别HTML表格内容,只要把MIME类型设置为application/vnd.ms-excel,文件扩展名写成.xls,浏览器下载后Excel就能打开并展示表格样式。这种方式比CSV多了边框、表头底色等基本格式,但本质仍然是HTML,文件结构不是真正的xlsx。
const htmlContent = `
<html>
<head><meta charset="UTF-8"></head>
<body>
<table border="1">
<tr><th>姓名</th><th>年龄</th></tr>
<tr><td>张三</td><td>28</td></tr>
</table>
</body>
</html>`;
downloadFile(htmlContent, '用户列表.xls', 'application/vnd.ms-excel');
二、使用SheetJS生成标准xlsx文件
如果项目还需要导出多个工作表、处理日期和数字格式,或者希望文件能被其他工具无缝读取,推荐使用SheetJS。SheetJS的社区版通常以xlsx作为全局变量暴露,可以从CDN引入,也可以npm安装后打包。它支持xlsx、xls、csv等多种格式的读写,能够把HTML表格、JSON数据、数组数据转换为标准的工作簿对象,再用writeFile直接触发浏览器下载。这个库在前端社区被广泛使用,文档和示例都比较丰富。
<script src="https://cdn.sheetjs.com/xlsx-0.20.2/package/dist/xlsx.full.min.js"></script>
最直接的用法是把页面上已有的表格元素导出为xlsx文件。table_to_book函数会读取表格的行列结构,并生成一个包含单个工作表的工作簿对象。这种方式适合已经用HTML表格展示数据的后台页面,不需要额外构造数据结构,几行代码就能完成导出。
function exportTableToExcel(tableId, filename) {
const table = document.getElementById(tableId);
const wb = XLSX.utils.table_to_book(table, { sheet: 'Sheet1' });
XLSX.writeFile(wb, filename || '表格数据.xlsx');
}
如果数据来自接口返回的JSON数组,可以用json_to_sheet直接生成工作表,再手动设置列宽。SheetJS社区版对单元格样式支持有限,但列宽、行列数、单元格类型这些基础结构都可以控制。下面的代码展示了从JSON数组导出并设置三列宽度的完整过程,数字和字符串会被自动识别为合适的单元格类型。
const data = [
{ name: '张三', age: 28, city: '北京' },
{ name: '李四', age: 32, city: '上海' }
];
const ws = XLSX.utils.json_to_sheet(data);
ws['!cols'] = [{ wch: 10 }, { wch: 8 }, { wch: 12 }];
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, '用户信息');
XLSX.writeFile(wb, '用户数据.xlsx');
SheetJS的好处是生成的文件是真正的xlsx格式,可以被Excel、WPS、LibreOffice等软件正确识别,支持多工作表、日期、公式等复杂内容。代价是引入库文件会让页面脚本体积增加几百KB,而且社区版对背景色、字体、边框等高级样式支持不足,需要Pro版才能精细控制。对大多数内部报表和后台导出来说,社区版已经足够。
三、中文乱码、旧浏览器与多Sheet导出技巧
使用CSV导出时,中文乱码是最常遇到的问题。Excel在打开UTF-8编码的CSV时,如果没有字节顺序标记,会默认按本地编码解析,导致中文显示为乱码。解决办法很简单,在CSV字符串开头加上BOM字符\ufeff,再生成Blob即可。这个字符是不可见的,但能告诉Excel文件是UTF-8编码。
const csvWithBom = '\ufeff' + csv; downloadFile(csvWithBom, '用户列表.csv', 'text/csv;charset=utf-8');
旧版浏览器也需要单独处理。IE10和早期的Edge不支持a标签的download属性,但提供了navigator.msSaveBlob方法,可以直接保存Blob对象。写导出函数时可以先检测这个方法是否存在,存在则调用msSaveBlob,不存在再走a标签下载的逻辑。这样能覆盖绝大多数遗留浏览器环境。
if (navigator.msSaveBlob) {
navigator.msSaveBlob(blob, filename);
} else {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
需要导出多个工作表时,使用SheetJS会非常方便。先创建一个空的工作簿对象,再分别生成两个工作表,最后通过book_append_sheet把工作表按顺序加入工作簿,writeFile时会自动生成包含多个Sheet的xlsx文件。用户打开后可以在Excel底部看到不同的Sheet标签页。
const wb = XLSX.utils.book_new();
const ws1 = XLSX.utils.json_to_sheet([{ a: 1 }, { a: 2 }]);
const ws2 = XLSX.utils.json_to_sheet([{ b: 'x' }, { b: 'y' }]);
XLSX.utils.book_append_sheet(wb, ws1, '一月');
XLSX.utils.book_append_sheet(wb, ws2, '二月');
XLSX.writeFile(wb, '多工作表.xlsx');
实际项目中,如果单次导出的数据量特别大,比如超过几万行,直接在主线程里生成文件和触发下载可能会让页面卡顿。这时可以把数据转换和Blob生成放进Web Worker中异步处理,完成后再把Blob传回主线程触发下载。另外,如果业务对导出格式有严格要求,或者需要服务端记录导出日志,也可以保留后端导出接口作为兜底方案,前端导出更适合数据量适中、格式简单的场景。综合来看,根据项目规模选择合适的方案,比一味追求纯前端实现更重要。
html5导出excel前端导出ExcelSheetJS修改时间:2026-10-05 10:41:50