1. 技术选型与核心原理
选择 exceljs 而非 SheetJS(xlsx)的核心考量在于格式处理能力 和流式写入支持:
- SheetJS 更擅长纯数据导出,对复杂格式(如合并单元格、条件格式)的支持有限,且 API 偏向底层,需手动拼接格式配置;
- exceljs 原生支持丰富的格式操作(如单元格样式、边框、字体、条件格式),API 设计更直观(如
cell.style
直接配置),且支持流式写入(stream.xlsx.writeBuffer()
),适合大文件导出。
例如,保留统计信息时,exceljs 可直接通过cell.value = { formula: 'SUM(A1:A10)', result: 100 }
同时定义公式和计算结果,而 SheetJS 需手动处理公式字符串,且预览时可能无法实时显示结果。
2. 复杂表格格式导出的实现
关键步骤:
-
解析原表格结构 :通过 DOM 遍历获取表格的
rowspan
/colspan
(合并单元格)、class
/style
(样式)、条件格式规则(如[data-value=">100"]
); -
映射 Excel 格式:
-
合并单元格:使用
worksheet.mergeCells(startRow, startCol, endRow, endCol)
; -
条件格式:通过
worksheet.addConditionalFormatting()
定义规则,例如:javascript
运行
phpworksheet.addConditionalFormatting({ ref: 'A1:A100', rules: [{ type: 'cellIs', operator: 'greaterThan', formula: '100', style: { font: { color: { argb: 'FF0000' } } } }] });
-
边框 / 底色:直接配置
cell.style
的border
和fill
属性。
-
兼容性问题:
- 旧版 Excel(如 2007)不支持部分条件格式类型(如数据条),需降级为基础样式(如纯色填充);
- Mac 版 Excel 对自定义边框的渲染可能存在偏差,需通过预设边框样式(如
thin
/medium
)规避。
3. 统计信息的动态计算与导出
两种方案对比:
- 前端预计算:优点是避免 Excel 公式依赖,导出后数据即时可见;缺点是大数据量时计算耗时,且修改原始数据后统计值需重新计算。
- Excel 公式生成:优点是动态联动(修改单元格后统计值自动更新),适合用户需二次编辑的场景;缺点是导出文件体积略大,且部分场景(如复杂聚合逻辑)难以用公式实现。
大数据量(10 万行)优化:
- 优先选择Excel 公式,减少前端计算压力;
- 对非联动场景,采用 Web Worker 预计算统计值,避免阻塞主线程;
- 分批次写入 Excel(每 1 万行 flush 一次),降低内存占用。
4. 导出模块的封装设计
API 设计原则:简洁易用 + 可扩展,对外暴露:
javascript
运行
css
// Excel导出核心API
exportExcel({
data: [{ id: 1, name: 'xx' }], // 原始数据
columns: [{ key: 'id', label: 'ID', width: 10 }], // 列配置
formatters: { name: (v) => `[${v}]` }, // 单元格格式化
stats: [{ type: 'sum', column: 'amount', label: '合计' }], // 统计配置
styles: { header: { fill: { type: 'pattern', pattern: 'solid', fgColor: { argb: 'F0F0F0' } } }, // 样式配置
onProgress: (percent) => {} // 进度回调
});
可扩展性设计:
-
采用插件化架构,将格式处理、统计计算、样式应用拆分为独立插件,例如:
javascript
运行
javascriptclass ExcelExporter { constructor() { this.plugins = [new MergeCellPlugin(), new ConditionalFormatPlugin()]; } use(plugin) { this.plugins.push(plugin); } // 动态添加插件 export() { this.plugins.forEach(p => p.apply(this.worksheet)); } }
-
复杂表格解析:通过适配器模式适配不同数据源(如数组、树形结构),定义
transformData
接口统一转换为二维表格结构。
5. Word 导出中的图表与富文本处理
图表插入完整流程:
-
图表截图:
- ECharts 通过
chart.getDataURL('png')
直接获取 Base64; - 其他图表(如 Canvas 绘制)使用
canvas.toDataURL('image/png', 1.0)
; - 解决跨域:确保图表资源同域,或配置
crossOrigin="anonymous"
。
- ECharts 通过
-
图片处理:
- 压缩图片(如使用
image-compressor
库),避免大图片导致 Word 体积过大; - 转换 Base64 为二进制(
atob
解码后转为Uint8Array
)。
- 压缩图片(如使用
-
docxtemplater 渲染:
-
配置 ImageModule:
new ImageModule({ centered: false })
; -
模板中使用
{@chartImage}
占位符,传入图片数据:javascript
运行
phpconst doc = new Docxtemplater(zip, { modules: [new ImageModule()] }); doc.render({ chartImage: { data: imageBuffer, width: 500, height: 300 } });
-
解决图表模糊:
- 截图时设置 2 倍分辨率(如
canvas.width = 1000
但样式width: 500px
),导出后自动适配 Word 缩放。
6. 富文本内容的格式兼容
实现方式:HTML 到 Word XML 的转换:
-
解析富文本 HTML:使用
DOMParser
遍历节点,识别标签(b
/i
/ul
/table
等); -
映射 Word 格式:
- 基础样式:
b
→<w:b/>
,span style="color:red"
→<w:color w:val="FF0000"/>
; - 列表:
ul
→<w:numPr><w:numId w:val="1"/></w:numPr>
(需预设编号样式); - 表格:通过
docxtemplater
的表格语法{#table}{name}{/table}
映射,结合w:tbl
相关 XML 配置边框。
- 基础样式:
-
自定义转换器:对复杂格式(如代码块、公式),开发自定义转换函数,例如:
javascript
运行
javascriptconst convertors = { 'pre': (node) => `<w:p><w:r><w:t>${node.textContent}</w:t></w:r></w:p>` };
格式丢失处理:
- 对不支持的标签(如
video
),降级为文字描述(如 "[视频内容]"); - 使用
html-docx-js
等辅助库处理边缘场景,结合手动修补 XML。
7. 大文件导出的性能优化
核心策略:
-
分批次处理:
- Excel:每 5000 行调用
worksheet.commit()
提交数据,释放内存; - Word:拆分长文本为多个段落,避免一次性渲染超大数据。
- Excel:每 5000 行调用
-
Web Worker 隔离:将数据处理、格式转换放入 Worker,主线程仅处理 UI 反馈(如进度条),避免页面卡顿:
javascript
运行
ini// 主线程 const worker = new Worker('export-worker.js'); worker.postMessage({ data: bigData }); worker.onmessage = (e) => { download(e.data.buffer); };
-
二进制流优化:
- 使用
stream
模式生成文件(如 exceljs 的stream.xlsx
),避免一次性占用大量内存; - 大图片采用延迟加载,仅在写入时解码 Base64。
- 使用
效果数据:10 万行 Excel 导出从卡顿 10s 优化至 3s 内完成,内存占用从 800MB 降至 200MB 以下。
8. 异常处理与容错机制
异常捕获与反馈:
- 数据源错误:通过 Schema 校验(如 Zod)提前检测数据格式,返回具体错误字段(如 "amount 字段应为数字");
- 浏览器不支持 :检测
Blob
/URL.createObjectURL
支持性,IE11 降级为msSaveOrOpenBlob
; - 图片加载失败 :监听图片
onerror
事件,使用默认占位图替代,同时在导出日志中标记。
重试机制:
- 对网络相关错误(如图片跨域加载失败),实现最多 3 次重试,间隔 1s;
- 重试失败后,提供 "跳过错误资源" 选项,确保基础内容可导出。
降级方案:
- 部分数据失败时,导出 "成功数据 + 错误报告"(如 Excel 最后一页列出失败项),避免全量失败。
9. 跨浏览器兼容性处理
核心适配点:
- Chrome/Firefox :支持
URL.createObjectURL(blob)
,直接通过<a download>
触发下载; - Safari :对大文件
blob
处理存在限制,需使用FileReader
转换为data:
URL(限 200MB 以内); - IE11 :不支持
Blob
构造函数和URL.createObjectURL
,需用msSaveOrOpenBlob
,且不支持流式处理,需全量生成后导出。
性能差异与优化:
- IE11 处理 10 万行 Excel 时内存溢出风险高,限制单次导出最大行数(如 5 万行),提供分批导出选项;
- Safari 对 Base64 图片解码较慢,提前在 Worker 中完成解码,减少主线程耗时。
10. 需求拓展与技术演进
Excel 导入还原:
- 引入
read-excel-file
解析 Excel,结合xlsx
读取格式信息(如合并单元格、样式); - 定义与导出模块对应的反向映射规则(如公式→统计逻辑,单元格样式→CSS),还原为页面表格。
Word 模板预览:
- 基于
docx-preview
库实现在线预览,结合pdf-lib
提供 PDF 导出选项; - 扩展模板引擎,支持实时编辑→预览→导出的闭环。
架构设计:
- 抽象 "文档操作核心层",统一管理 Excel/Word 的解析、渲染、转换逻辑;
- 定义格式描述协议(如 JSON Schema 描述表格样式),确保导入 / 导出 / 预览使用同一套格式规则;
- 引入状态管理(如 Redux)保存文档操作历史,支持撤销 / 重做。