优先用 xlsx.writefile 或加 bom 头的 blob 导出 csv,避免手动拼接导致中文乱码、错列等问题;关键点包括添加 \ufeff bom 头、正确转义双引号、过滤隐藏行列、处理 ios 降级等。

直接用原生 JS 手写 CSV 拼接,90% 的人会在中文乱码、逗号换行转义、隐藏行列处理上翻车。真要稳定导出,优先走 XLSX.writeFile(导 Excel)或加 BOM 头的 Blob(导 CSV),别硬刚字符串拼接。
为什么手动拼接 CSV 容易打开就错列或乱码
Excel 默认用 ANSI 打开 CSV,不加 \uFEFF BOM 头,中文就是乱码;单元格里有 ,、\n 或 " 却没包裹双引号+转义内部引号,Excel 就会把一行切错成多列。
- 错误现象:导出后 Excel 显示“#VALUE!”、列偏移、中文变问号
- 常见漏点:只做
replace(/"/g, '""'),却没判断是否需要加双引号包裹 - 正确逻辑必须是:
value.includes(',') || value.includes('\n') || value.includes('"') ? `"${value.replace(/"/g, '""')}"` : value -
innerText比textContent更安全(忽略 script/style 内容),但会折叠空白符;innerHTML绝对禁用,否则标签代码直接进 CSV
怎么用 Blob + BOM 正确导出 CSV
这是轻量、无依赖、兼容性最好的纯前端 CSV 导出方式,关键在 BOM 头和 MIME 类型。
- 必须在字符串开头加
\uFEFF:例如const csv = '\uFEFF' + rows.map(...).join('\n'); -
Blob的 type 必须写全:type: 'text/csv;charset=utf-8',缺charset=utf-8Safari 和部分 Excel 仍会乱码 - 生成链接后,
a元素必须 append 到document.body才能触发click(),否则 Chrome 静默失败 - 下载完务必调用
URL.revokeObjectURL(url),否则内存泄漏在长时间操作页面时明显
怎么跳过 display:none 的行和列
DOM 遍历时不能靠 CSS 属性判断可见性,visibility: hidden 的单元格依然会被读取,而 display: none 的则不会出现在 querySelectorAll('tr') 结果中 —— 但得注意:它只影响元素是否在渲染树中,不影响 DOM 存在。
- 安全做法是遍历前先过滤:
Array.from(table.querySelectorAll('tr')).filter(tr => getComputedStyle(tr).display !== 'none') - 列级隐藏更麻烦:没有“列元素”,只能检查每行中对应索引的
td是否被style.display = 'none'或父colgroup控制,实际建议统一用data-export="false"标记跳过 - 不要用
offsetParent === null判断,它在某些布局下不可靠
移动端 Safari 不支持 download 属性怎么办
iOS Safari 无视 a.download,点击只会打开新页显示 CSV 内容。必须降级为长按「复制」或「分享」操作。
- 检测方式:
typeof document.createElement('a').download === 'undefined' - 降级方案:生成 CSV 后,用
navigator.clipboard.writeText(csv)复制到剪贴板,并提示用户粘贴到表格软件 - 注意:
writeText是异步 Promise,需await,且需用户手势触发(比如在 click 回调里) - 不推荐用
location.href = 'data:text/csv,...',iOS 上可能被拦截或打不开
真正难的不是写出能跑的代码,而是覆盖所有边界:含双引号的姓名、带换行的备注、隐藏列里的统计值、Safari 下的降级体验——这些细节堆起来,才是生产环境里“导出按钮”敢上线的关键。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











