如何使用 JavaScript 创建 Excel 文件(XLSX/XLS)

JavaScript 可以直接在浏览器中生成 Excel 工作簿。借助 Spire.XLS for JavaScript,你可以创建工作簿、添加工作表、写入数据和公式、应用格式,并将结果保存为 XLSX 或 XLS 文件——全部在客户端完成,无需安装 Microsoft Excel。

本教程从一个基础的 XLSX 工作簿开始,逐步引入带类型的单元格写入、公式、格式化、多工作表、浏览器下载、CSV 转换以及 XLS 格式输出。

快速导航:

安装并初始化 Spire.XLS for JavaScript

Spire.XLS for JavaScript 包含在 spire.office 包中,与 Spire.PDF、Spire.Doc 和 Spire.Presentation 一起发布:

1
npm i spire.office

初始化运行时需要两步导入。第一步启动共享的 .NET WebAssembly 宿主机,第二步注册电子表格 API:

1
2
3
4
5
6
// 1. 每个页面只需启动一次共享运行时
const common = await import('/node_modules/spire.office/spire.common.js');
await common.initializeWasm();

// 2. 加载电子表格引擎——这一步会创建 window.spirexls
await import('/node_modules/spire.office/spire.xls.js');

Spire.*.Wasm.zip 压缩包和 _framework 文件夹必须能从站点根目录访问。运行时基于文档 URL 解析这些文件,而不是基于执行导入的模块路径,因此在 Vite 或 Create React App 项目中需要将它们放在 public/ 目录下。React 项目中常见的 process.env.PUBLIC_URL 前缀是 Create React App 的约定,并非浏览器标准——如果未使用 CRA,请根据构建工具调整基础路径。如果压缩包缺失,浏览器会输出 WebAssembly.compile(): expected magic word——这是因为开发服务器用 index.html 响应了压缩包请求。

之后所有操作都通过一个全局对象进行:

1
const xls = window.spirexls;

本教程使用的当前包版本中,电子表格 API 通过 window.spirexls 暴露。在运行时初始化完成后即可使用该全局对象。旧版本通过 window.wasmModule.spirexls 暴露;如果使用不同的包版本,请检查可用的全局变量。

现在可以开始生成 Excel 文件了。

在 JavaScript 中创建基础 Excel 文件

我们来构建一份销售报表。创建工作簿、添加工作表、将产品数据写入单元格,然后保存为 XLSX 文件并自动下载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
async function createExcelFile() {
const xls = window.spirexls;
if (!xls) {
console.error('Spire.XLS 尚未初始化');
return;
}

// 新建的 Workbook() 默认包含三个空工作表,先清空
// 再添加本报表所需的单个工作表
const workbook = new xls.Workbook();
workbook.Worksheets.Clear();
const sheet = workbook.Worksheets.Add("销售报表");

// 示例数据:产品销售
const data = [
["产品", "数量", "单价"],
["笔记本电脑", 10, 999.99],
["无线鼠标", 50, 24.99],
["机械键盘", 30, 59.99],
["显示器", 15, 329.99]
];

// 中文内容统一用「等线」——默认字体是 Arial,汉字会兜底成宋体
sheet.Range.get("A1:C5").Style.Font.FontName = "等线";

// 将数据写入单元格
for (let row = 0; row < data.length; row++) {
for (let col = 0; col < data[row].length; col++) {
const cell = sheet.Range.get({ row: row + 1, column: col + 1 });
if (typeof data[row][col] === "string") {
cell.Text = data[row][col];
} else {
cell.NumberValue = data[row][col];
}
}
}

// 保存到虚拟文件系统,读取字节,下载,然后释放
const fileName = "SalesReport.xlsx";
workbook.SaveToFile({
fileName: fileName,
version: xls.ExcelVersion.Version2016
});

const fileData = window.dotnetRuntime.Module.FS.readFile(fileName);
const blob = new Blob([fileData], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = fileName;
a.click();
URL.revokeObjectURL(url);

workbook.Dispose();
}

调用 createExcelFile() 即可得到包含四行产品数据和一行表头的 SalesReport.xlsx。整个流程很简洁:创建工作簿 → 写入数据 → 保存 → 下载。

后续各节将在此基础上扩展。每个代码片段都假定在 createExcelFile() 中工作簿和工作表已创建之后添加。

由 createExcelFile() 函数生成的 SalesReport.xlsx

向 Excel 单元格写入不同数据类型

Excel 区分文本、数字、日期和布尔值。类型使用不当会导致排序失效、公式报错、数字以文本形式显示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const sheet = workbook.Worksheets.get(0);

// 文本——用于标签、名称、描述
sheet.Range.get("A1").Text = "产品名称";

// 数字——用于需要计算、排序或筛选的数据
sheet.Range.get("B1").NumberValue = 999.99;

// 日期——使用 DateTimeValue 配合显示格式
const dateCell = sheet.Range.get("C1");
dateCell.DateTimeValue = new Date(Date.UTC(2025, 2, 15));
dateCell.NumberFormat = "yyyy-mm-dd";

// 布尔值——使用 BooleanValue,而非文本
sheet.Range.get("D1").BooleanValue = true;

最常见的错误是写 cell.Text = "999.99" 而非 cell.NumberValue = 999.99。打开文件时数值看起来一样,但 Excel 会将其视为文本——无法求和、求平均或按数值排序。始终用 NumberValue 写数字、DateTimeValue 写日期、BooleanValue 写布尔值。

日期还需要一个额外注意点。DateTimeValue 保留的是 JavaScript Date 所表示的 UTC 时间点。用本地午夜创建日期在某些时区下会导致显示的日期偏移;若要保留特定的日历日期,请使用 Date.UTC()。

为 Excel 工作表添加公式

公式让生成的文件成为真正的电子表格,而不只是数据堆砌。我们来添加一个「金额」列计算每行的 数量 × 单价,并在底部添加合计行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 添加「金额」表头
sheet.Range.get({ row: 1, column: 4 }).Text = "金额";

// 每行公式:金额 = 数量 × 单价
for (let i = 2; i <= 5; i++) {
sheet.Range.get({ row: i, column: 4 }).Formula = `=B${i}*C${i}`;
}

// 合计行
sheet.Range.get({ row: 6, column: 1 }).Text = "合计";
sheet.Range.get({ row: 6, column: 2 }).Formula = "=SUM(B2:B5)";
sheet.Range.get({ row: 6, column: 4 }).Formula = "=SUM(D2:D5)";

// 预先计算公式,将结果写入文件
workbook.CalculateAllValue();

当需要生成的工作簿包含公式的计算结果时,在保存前调用 workbook.CalculateAllValue()。这对于依赖缓存值而非打开时重新计算的查看器或应用程序很有用。

如需深入了解 Excel 函数和公式操作,请参阅在 React 中使用 JavaScript 在 Excel 工作表中插入或读取函数和公式。

格式化生成的 Excel 文件

只有原始数据的表格能用,但格式化的表格才能传达信息。我们把销售报表变成一份可以直接发给同事的文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 粗体、彩色表头行
const header = sheet.Range.get("A1:D1");
header.Style.Font.IsBold = true;
header.Style.Font.Size = 12;
header.Style.Color = xls.Color.get_LightSkyBlue();

// 单价和金额列设置人民币格式
for (let i = 2; i <= 5; i++) {
sheet.Range.get({ row: i, column: 3 }).NumberFormat = "¥#,##0.00";
sheet.Range.get({ row: i, column: 4 }).NumberFormat = "¥#,##0.00";
}

// 显式设置列宽
[26, 10, 12, 12].forEach((width, i) => {
sheet.Columns.get(i).ColumnWidth = width;
});

// 整洁的边框
const usedRange = sheet.Range.get("A1:D6");
usedRange.Borders.LineStyle = xls.LineStyleType.Thin;
usedRange.Borders.Color = xls.Color.get_LightSteelBlue();

有两个细节值得注意。sheet.Columns.get(i) 和 sheet.Rows.get(i) 是 0 基索引并使用 get 方法,与其他集合暴露的 get_Item 不同。此外,在测试的浏览器环境中,AutoFitColumn 需要的字体在 WebAssembly 沙箱中不可用,因此显式设置 ColumnWidth 更可靠。

最终效果:粗体蓝色表头、货币格式的价格、宽度合适的列以及整洁的边框。

如需了解单元格边框的详细设置方法,请参阅在 React 中使用 JavaScript 添加或删除单元格边框。

在 Excel 工作簿中创建多个工作表

实际报表很少只放在一个工作表上。销售报表可能需要在第一个标签页放摘要,第二个放产品明细,第三个放月度分解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
workbook.Worksheets.Clear();
const summarySheet = workbook.Worksheets.Add("汇总");
const productsSheet = workbook.Worksheets.Add("产品明细");
const monthlySheet = workbook.Worksheets.Add("月度数据");

// 产品明细工作表——写入实际数据,使跨表公式能正常工作
productsSheet.Range.get("A1").Text = "产品";
productsSheet.Range.get("B1").Text = "数量";
productsSheet.Range.get("C1").Text = "单价";
productsSheet.Range.get("D1").Text = "金额";

const products = [
["笔记本电脑", 10, 999.99],
["无线鼠标", 50, 24.99],
["机械键盘", 30, 59.99],
["显示器", 15, 329.99]
];

for (let i = 0; i < products.length; i++) {
const row = i + 2;
productsSheet.Range.get({ row: row, column: 1 }).Text = products[i][0];
productsSheet.Range.get({ row: row, column: 2 }).NumberValue = products[i][1];
productsSheet.Range.get({ row: row, column: 3 }).NumberValue = products[i][2];
productsSheet.Range.get({ row: row, column: 4 }).Formula = `=B${row}*C${row}`;
}

// 汇总工作表,使用跨表引用
summarySheet.Range.get("A1").Text = "销售汇总";
summarySheet.Range.get("A1").Style.Font.IsBold = true;
summarySheet.Range.get("A2").Text = "产品总数";
summarySheet.Range.get("B2").NumberValue = 4;
summarySheet.Range.get("A3").Text = "销售总额";
summarySheet.Range.get("B3").Formula = "=SUM(产品明细!D2:D5)";

// 中文内容统一用「等线」(同上)
productsSheet.Range.get("A1:D5").Style.Font.FontName = "等线";
summarySheet.Range.get("A1:B3").Style.Font.FontName = "等线";

每次 Worksheets.Add(name) 都会按调用顺序返回新创建的工作表,因此标签页顺序与代码顺序一致。注意跨表公式:汇总工作表中的 =SUM(产品明细!D2:D5) 引用了产品明细工作表的 金额 列。Excel 在文件打开时自动处理这类引用——无需额外代码。

包含汇总和产品明细标签页的多工作表工作簿

如需了解更多工作表管理操作——添加、删除和重排工作表,请参阅在 React 中使用 JavaScript 添加、删除、移动 Excel 工作表。

在浏览器中保存并下载 XLSX 文件

工作簿准备好之后,Spire.XLS 会将其保存到 WebAssembly 虚拟文件系统中。然后读取文件数据、转换为 Blob 并触发下载:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
workbook.SaveToFile({
fileName: "Report.xlsx",
version: xls.ExcelVersion.Version2016
});

const fileData = window.dotnetRuntime.Module.FS.readFile("Report.xlsx");
const blob = new Blob([fileData], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "Report.xlsx";
a.click();
URL.revokeObjectURL(url);

workbook.Dispose();

务必在下载后调用 workbook.Dispose() 释放内存——尤其是在用户可能在一个会话中生成多个文件的应用中。工作簿本身驻留在 WebAssembly 堆中,直到你显式释放,而该堆不会被浏览器的垃圾回收器自动回收。

需要 React 集成或 HTML 表格导出?请参阅我们的如何在 JavaScript 和 React 中下载/导出 Excel 文件指南。

综合示例:格式化销售报表

前面各节分别处理了工作簿的一个方面。下面将它们整合到一个函数中:一个带斑马纹和货币格式的工作表、由公式驱动的收入和占比列、一个柱形图,以及一个汇总数据的第二个工作表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/**
* 在浏览器中构建格式化销售报表工作簿并下载为
* SalesReport.xlsx。
*
* 布局由下面的 products 数组驱动——替换为表单输入、API
* 响应或组件状态,其余部分无需修改。
*/
async function createExcelReport() {
// spire.office 11.7.0 将 Spire.XLS 暴露为 window.spirexls
// 旧版本通过 window.wasmModule.spirexls 暴露,该全局已不存在
const xls = window.spirexls;
if (!xls) throw new Error('Spire.XLS 尚未就绪');

// [产品, 销量, 单价]——销售额和占比由公式推导
const products = [
['星辰 14 轻薄本', 42, 6499.0],
['云雀 无线鼠标', 380, 149.0],
['磐石 机械键盘', 165, 399.0],
['流光 27 英寸 4K 显示器', 74, 2699.0],
['山海 USB-C 扩展坞', 210, 799.0],
['静默 主动降噪耳机', 128, 1299.0],
];
const FIRST = 5; // 第一行数据
const TOTAL = FIRST + products.length; // 合计行

const wb = new xls.Workbook();
wb.Worksheets.Clear();
const sheet = wb.Worksheets.Add('销售报表');

const at = (a) => sheet.Range.get(a);
const cell = (row, col) => sheet.Range.get({ row: row, column: col });
const paint = (a, colour) => {
at(a).Style.Color = colour;
};

// ── 列宽 ── 显式设置(AutoFitColumn 在 WASM 沙箱中不可靠)
[34, 9, 13, 14, 9].forEach((w, i) => {
sheet.Columns.get(i).ColumnWidth = w;
});
sheet.Rows.get(0).RowHeight = 34; // 标题
sheet.Rows.get(1).RowHeight = 20; // 副标题
sheet.Rows.get(2).RowHeight = 8; // 间隔
sheet.Rows.get(3).RowHeight = 24; // 表头

// ── 标题栏 ── 先合并,再统一样式
at('A1:E1').Merge();
cell(1, 1).Text = '销售报表 — 2026 年第三季度';
paint('A1:E1', xls.Color.get_DarkBlue());
at('A1:E1').Style.Font.Color = xls.Color.get_White();
at('A1:E1').Style.Font.IsBold = true;
at('A1:E1').Style.Font.Size = 15;
at('A1:E1').Style.VerticalAlignment = xls.VerticalAlignType.Center;

at('A2:E2').Merge();
cell(2, 1).Text = '区域:华东 · 周期:2026 年 7–9 月 · 金额单位:人民币(元)';
paint('A2:E2', xls.Color.get_DarkBlue());
at('A2:E2').Style.Font.Color = xls.Color.get_LightSteelBlue();
at('A2:E2').Style.Font.Size = 9.5;
at('A2:E2').Style.VerticalAlignment = xls.VerticalAlignType.Center;

// ── 表头行 ───────────────────────────────────────────────────────────
['产品', '销量', '单价', '销售额', '占比'].forEach((label, i) => {
cell(4, i + 1).Text = label;
});
paint('A4:E4', xls.Color.get_LightSteelBlue());
at('A4:E4').Style.Font.Color = xls.Color.get_DarkBlue();
at('A4:E4').Style.Font.IsBold = true;
at('A4:E4').Style.Font.Size = 10.5;
at('A4:E4').Style.HorizontalAlignment = xls.HorizontalAlignType.Center;
at('A4:E4').Style.VerticalAlignment = xls.VerticalAlignType.Center;

// ── 数据行 ── 数字用 NumberValue 写入,不用文本
products.forEach(([name, units, price], i) => {
const r = FIRST + i;
cell(r, 1).Text = name;
cell(r, 2).NumberValue = units;
cell(r, 3).NumberValue = price;
cell(r, 4).Formula = `=B${r}*C${r}`;
cell(r, 5).Formula = `=D${r}/$D$${TOTAL}`; // 占合计的百分比
sheet.Rows.get(r - 1).RowHeight = 20;
if (i % 2) paint(`A${r}:E${r}`, xls.Color.get_WhiteSmoke()); // 斑马纹
});

// ── 合计行 ────────────────────────────────────────────────────────────
cell(TOTAL, 1).Text = '合计';
[2, 4, 5].forEach((col) => {
const letter = String.fromCharCode(64 + col);
cell(TOTAL, col).Formula = `=SUM(${letter}${FIRST}:${letter}${TOTAL - 1})`;
});
paint(`A${TOTAL}:E${TOTAL}`, xls.Color.get_LightSkyBlue());
at(`A${TOTAL}:E${TOTAL}`).Style.Font.IsBold = true;
sheet.Rows.get(TOTAL - 1).RowHeight = 22;

// ── 数字格式和边框 ───────────────────────────────────────────
at(`B${FIRST}:B${TOTAL}`).NumberFormat = '#,##0';
at(`C${FIRST}:D${TOTAL}`).NumberFormat = '¥#,##0.00';
at(`E${FIRST}:E${TOTAL}`).NumberFormat = '0.0%';

const table = at(`A4:E${TOTAL}`);
table.Borders.LineStyle = xls.LineStyleType.Thin;
table.Borders.Color = xls.Color.get_LightSteelBlue();
at(`A${TOTAL}:E${TOTAL}`).Borders.get_Item(xls.BordersLineType.EdgeTop).LineStyle =
xls.LineStyleType.Medium;

// ── 图表 ── 手动构建系列(DataRange 会混换单位)
const chart = sheet.Charts.Add();
chart.ChartType = xls.ExcelChartType.ColumnClustered;
chart.LeftColumn = 6;
chart.TopRow = 3;
chart.RightColumn = 13;
chart.BottomRow = 21;
const serie = chart.Series.Add();
serie.CategoryLabels = at(`A${FIRST}:A${TOTAL - 1}`);
serie.Values = at(`D${FIRST}:D${TOTAL - 1}`);
serie.Name = '销售额';
chart.ChartTitleArea.Text = '各产品销售额';
chart.HasLegend = false;
chart.PrimaryValueAxis.NumberFormat = '¥#,##0';

// ── 工作表外观 ──
sheet.FreezePanes(FIRST, 1);
sheet.GridLinesVisible = false;
sheet.TabColor = xls.Color.get_DarkBlue();

// ── 第二个工作表:跨表汇总 ──
const summary = wb.Worksheets.Add('汇总');
summary.Columns.get(0).ColumnWidth = 26;
summary.Columns.get(1).ColumnWidth = 18;
summary.Rows.get(0).RowHeight = 30;
summary.Rows.get(1).RowHeight = 18;
summary.Rows.get(3).RowHeight = 22;

const band = (a, text, size) => {
summary.Range.get(a).Merge();
summary.Range.get(a.split(':')[0]).Text = text;
summary.Range.get(a).Style.Color = xls.Color.get_DarkBlue();
summary.Range.get(a).Style.Font.Color = xls.Color.get_White();
summary.Range.get(a).Style.Font.IsBold = true;
summary.Range.get(a).Style.Font.Size = size;
summary.Range.get(a).Style.VerticalAlignment = xls.VerticalAlignType.Center;
};
band('A1:B1', '报表摘要', 14);
band('A2:B2', '销售报表 · 2026 年第三季度', 9.5);

['指标', '数值'].forEach((label, i) => {
summary.Range.get({ row: 4, column: i + 1 }).Text = label;
});
summary.Range.get('A4:B4').Style.Color = xls.Color.get_LightSteelBlue();
summary.Range.get('A4:B4').Style.Font.Color = xls.Color.get_DarkBlue();
summary.Range.get('A4:B4').Style.Font.IsBold = true;

[
['销售总额', "='销售报表'!D" + TOTAL, '¥#,##0.00'],
['销售数量', "='销售报表'!B" + TOTAL, '#,##0'],
['平均单价', `='销售报表'!D${TOTAL}/'销售报表'!B${TOTAL}`, '¥#,##0.00'],
].forEach(([label, formula, format], i) => {
summary.Range.get({ row: 5 + i, column: 1 }).Text = label;
const value = summary.Range.get({ row: 5 + i, column: 2 });
value.Formula = formula;
value.NumberFormat = format;
value.Style.HorizontalAlignment = xls.HorizontalAlignType.Right;
if (i % 2) summary.Range.get(`A${5 + i}:B${5 + i}`).Style.Color = xls.Color.get_WhiteSmoke();
});

// 日期——使用 Date.UTC 避免时区偏移
summary.Range.get('A8').Text = '报表日期';
const reportDate = summary.Range.get('B8');
reportDate.DateTimeValue = new Date(Date.UTC(2026, 8, 28));
reportDate.NumberFormat = 'yyyy-mm-dd';
reportDate.Style.HorizontalAlignment = xls.HorizontalAlignType.Right;

const kpis = summary.Range.get('A4:B8');
kpis.Borders.LineStyle = xls.LineStyleType.Thin;
kpis.Borders.Color = xls.Color.get_LightSteelBlue();
summary.GridLinesVisible = false;
summary.TabColor = xls.Color.get_LightSteelBlue();

// ── 中文字体 ── 默认字体是 Arial,汉字会兜底成宋体,
// 统一改为「等线」;放在最后,不会覆盖已设好的字号和字色
at(`A1:E${TOTAL}`).Style.Font.FontName = '等线';
summary.Range.get('A1:B8').Style.Font.FontName = '等线';

// ── 保存,然后释放 ──
wb.CalculateAllValue();
const fileName = 'SalesReport.xlsx';
wb.SaveToFile({ fileName: fileName, version: xls.ExcelVersion.Version2016 });

const fileData = window.dotnetRuntime.Module.FS.readFile(fileName);
const blob = new Blob([fileData], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
});
const url = URL.createObjectURL(blob);
Object.assign(document.createElement('a'), { href: url, download: fileName }).click();
URL.revokeObjectURL(url);

wb.Dispose(); // 释放 WASM 堆——每次生成周期一个工作簿
}

布局由数据驱动。将 products 数组替换为你自己 API 的响应,其余部分——合计、占比、图表范围、汇总数据——全部继续正常工作,因为它们都是通过公式或派生范围表达的,而非硬编码的值。

包含图表、斑马纹和汇总工作表的完整格式化销售报表

如需了解更多图表类型和配置选项,请参阅在 React 中使用 JavaScript 创建 Excel 图表。

从 CSV 数据创建 Excel 文件

可以将 CSV 文件中的表格数据加载到工作表中,然后保存为 XLSX。生成的工作簿可以进一步格式化或添加公式和额外的工作表:

1
2
3
4
5
6
7
8
9
10
11
// 库从自身的虚拟文件系统读取文件,因此 CSV 文件
// 必须先写入 VFS——这里从字符串写入,实际应用中
// 可从 File 对象的字节数据写入
const csv = "产品,数量,单价\n笔记本电脑,10,999.99\n无线鼠标,50,24.99\n机械键盘,30,59.99";
window.dotnetRuntime.Module.FS.writeFile("data.csv", new TextEncoder().encode(csv));

workbook.LoadFromFile("data.csv", ",");
workbook.SaveToFile({
fileName: "ConvertedFromCSV.xlsx",
version: xls.ExcelVersion.Version2016
});

导入的工作表以文件名命名(data),因此 Worksheets.get(0) 即可获取。

分隔符是必需的第二个参数。单独调用 LoadFromFile("data.csv") 会被拒绝并返回 This is not a structured storage file,因为单参数重载期望的是 .xlsx 或 .xls 等结构化格式,不会自动识别 CSV。

还有第二个陷阱:CSV 导入器会将每个字段都写为文本。数量列到达时是 "10" 而非 10,这意味着无法求和或按数值排序——正是前面描述的问题。在保存前转换需要的列:

1
2
3
4
5
6
7
8
9
10
11
12
const csvSheet = workbook.Worksheets.get(0);

// 导入器将所有字段留为文本——对每个数值列进行类型转换
// 第 2、3 列分别是数量和单价,到达时为 "10" 和 "999.99"
for (let row = 2; row <= 4; row++) {
[2, 3].forEach((column) => {
const cell = csvSheet.Range.get({ row: row, column: column });
if (cell.Text !== "") {
cell.NumberValue = Number(cell.Text);
}
});
}

如需了解 CSV 与 Excel 格式之间的完整转换指南,请参阅我们的在 React 中使用 JavaScript 将 Excel 转换为 CSV 或将 CSV 转换为 Excel 教程。

创建 XLS 格式文件

如果应用需要生成旧版 .xls 格式而非 .xlsx,修改版本参数即可:

1
2
3
4
workbook.SaveToFile({
fileName: "Report.xls",
version: xls.ExcelVersion.Version97to2003
});

枚举成员是 Version97to2003——不存在 Version97。图表请保留在 XLSX 中;测试发现 XLS 写入器在需要图表字体度量时会失败。

XLSX(Excel 2007+)是新建应用的推荐格式。XLS(Excel 97–2003)仅在需要向后兼容时使用。

常见问题

window.wasmModule 为 undefined。 旧示例从 window.wasmModule.spirexls 读取电子表格 API。当前版本直接将其安装在 window.spirexls 上,wasmModule 保持 undefined,因此首次访问就会抛出异常。请改用 window.spirexls。

WASM 模块未初始化。 如果 window.spirexls 为 undefined,说明运行时尚未加载完成。显示加载指示器,等待初始化完成后再执行 Excel 操作。

自动调整列宽抛出字体错误。 AutoFitColumn 和 AutoFitRow 需要系统字体来测量文本,而 WebAssembly 沙箱中没有可用字体。它们会以 Cannot found font(Arial) installed on the system. 失败。请用 sheet.Columns.get(i).ColumnWidth 计算或硬编码列宽。

数字存储为文本。 使用 cell.Text = "100" 而非 cell.NumberValue = 100 会破坏排序和计算。这是开发者在用 JavaScript 写 Excel 文件时最常遇到的问题,CSV 导入也会自动触发——始终用 NumberValue 写入数值数据。

出现”Evaluation Warning”工作表。 使用未授权的评估版本时,Spire.XLS 会在保存的工作簿中添加一个评估工作表。该工作表在每次保存时都会重新创建,因此以编程方式移除并不可靠。请应用许可证密钥来防止此问题。

长时间运行应用中的内存泄漏。 在每个生成周期后调用 workbook.Dispose()。在单页应用中,未释放的工作簿会累积内存并导致性能逐渐下降。

FAQ

JavaScript 能在不安装 Microsoft Excel 的情况下创建 Excel 文件吗?

可以。Spire.XLS for JavaScript 通过 WebAssembly 完全在浏览器中运行,生成 XLSX 文件不需要 Microsoft Excel 或服务端 Office 安装。

JavaScript 能直接在浏览器中创建 XLSX 文件吗?

可以。所有电子表格操作都在客户端完成。文件保存到虚拟文件系统后,以 Blob 形式下载——不需要后端服务器。

XLS 和 XLSX 有什么区别?

XLSX(Excel 2007 及更高版本)是现代的基于 XML 的格式,推荐用于新建应用。XLS(Excel 97–2003)是旧版二进制格式,适用于旧系统兼容场景。

总结

用 JavaScript 创建 Excel 文件不需要后端服务器或 Microsoft Excel。借助 Spire.XLS for JavaScript,你可以在浏览器中从零构建工作簿、写入带类型的数据、添加公式、应用格式,并将数据组织到多个工作表中。

从上面的基础示例开始,随着需求增长再逐步加入公式和格式。如需 React 专用集成和 HTML 表格导出,请参阅我们的专门的导出教程。