只要同一份内容要发给不同的人,Word 的「邮件合并」就是最省事的办法:把会变的部分(收件人、传真号、日期)做成合并域留在模板里,其余排版固定不动,运行时把一组数据填进去即可。在浏览器端做这件事的价值在于——模板始终留在服务器,数据也不用交给 Office,用户点一下就能拿到填好的文档。
Spire.Doc for JavaScript 把合并域当作一类文档元素来解析,合并入口是 MailMerge.Execute(域名数组, 域值数组):两个数组按下标一一对应,模板里有、数组里没给的域会被合并成空。
本文介绍两个核心功能点:
有关安装和项目配置,请参考 React 项目中集成 Spire.Doc for JavaScript 的方法。以下示例默认已安装 Spire.Doc 并完成 WebAssembly 模块初始化。
执行邮件合并
准备模板
模板是一张普通的客户联络单,会随数据变化的四处都做成了合并域:
To: «Contact Name»
From: 客户服务部
Subject: 需求确认函
Fax.: «Fax» Date: «Date»
Message:
尊敬的 «Contact Name»:
您好,感谢贵司的回复。我们会在两个工作日内与您联系,确认具体的交付安排。
此致
敬礼
客户服务部
模板里出现的 «Contact Name»、«Fax»、«Date» 就是 Word 里合并域的占位符写法(输入域时按 Ctrl + F9,再填 MERGEFIELD 域名)。注意 Contact Name 里带空格,Word 在域代码中会把它写成带引号的形式 MERGEFIELD "Contact Name";后面代码里传域名时要把引号去掉,只写 "Contact Name" 这个名字本身。
代码
function App() {
const MailMerge = async () => {
const docModule = window.wasmModule?.spiredoc;
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 将示例模板载入虚拟文件系统(VFS)
let inputFileName = "MailMerge.docx";
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}static/data/`);
// 新建文档并载入模板
const doc = new docModule.Document();
doc.LoadFromFile(inputFileName);
// 域名与域值按下标一一对应
let fieldNames = ["Contact Name", "Fax", "Date"];
let fieldValues = ["John Smith", "+1 (69) 123456", docModule.DateTime.get_Now().Date.ToString()];
// 执行邮件合并
doc.MailMerge.Execute(fieldNames, fieldValues);
// 定义输出文件名
const outputFileName = "MailMerge-result.docx";
// 保存文档
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });
doc.Dispose()
// 读取保存的文件并转换为 Blob 对象
const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([modifiedFileArray], {type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>把数据合并到 Word 文档中</h1>
<button onClick={MailMerge}>开始</button>
</div>
);
}
export default App;
这里只有两行是合并逻辑本身:
document.MailMerge.Execute(fieldNames, fieldValues);
DateTime.get_Now().Date.ToString() 取的是当前日期,返回的是一个字符串,直接当作域值传进去即可——之后 Fax 与「Date: 」标签之间那段空白由模板决定,合并过程不会动它。
效果
四个域全部被替换掉,模板其余部分原样保留:
To: John Smith
From: 客户服务部
Subject: 需求确认函
Fax.: +1 (69) 123456 Date: 2026/10/10 上午12:00:00
Message:
尊敬的 John Smith:
您好,感谢贵司的回复。我们会在两个工作日内与您联系,确认具体的交付安排。
此致
敬礼
客户服务部
Contact Name 出现了两次,两次都被同一个值替换——域名相同的域共享同一个值,不需要在数组里列两遍。

合并时按区域设置格式化域值
上面那句 Date.ToString() 的输出格式由运行环境的文化设置决定。同一段代码在中文 Windows 上给出 2026/10/10 上午12:00:00,在英文环境下就会变成 10/10/2026 12:00:00 AM。如果收件方在德国,正确的写法应该是 10.10.2026, 10:50:45——日在前、月在中间、用点分隔。
要控制这个格式,最直接的做法是在值进入 Execute 之前就把它格式化好:合并本身只负责把一串文本放进域的位置,日期怎么排是数据准备阶段的事。
代码
与上一个示例共用同一份 MailMerge.docx 模板,只把日期换成按 de-DE 区域格式化后的字符串:
function App() {
const ChangeLocale = async () => {
const docModule = window.wasmModule?.spiredoc;
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 将示例模板载入虚拟文件系统(VFS)
let inputFileName = "MailMerge.docx";
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}static/data/`);
// 新建文档并载入模板
const doc = new docModule.Document();
doc.LoadFromFile(inputFileName);
// 按德国区域格式(日.月.年)准备日期字符串
const now = new Date();
const datestr = new Intl.DateTimeFormat("de-DE",
{
year: 'numeric', month: '2-digit', day: '2-digit',
hour: "2-digit", minute: '2-digit', second: "2-digit"
}).format(now);
// 域名与域值按下标一一对应
let fieldNames = ["Contact Name", "Fax", "Date"];
let fieldValues = ["John Smith", "+1 (69) 123456", datestr];
// 执行邮件合并
doc.MailMerge.Execute(fieldNames, fieldValues);
// 定义输出文件名
const outputFileName = "ChangeLocale-result.docx";
// 保存文档
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });
doc.Dispose()
// 读取保存的文件并转换为 Blob 对象
const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([modifiedFileArray], {type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>把德国区域格式的日期合并到 Word 文档中</h1>
<button onClick={ChangeLocale}>开始</button>
</div>
);
}
export default App;
模板完全相同,合并调用也完全相同,唯一变的是 fieldValues 里那个日期字符串的来源:
const datestr = new Intl.DateTimeFormat("de-DE", { /* … */ }).format(now);
Intl.DateTimeFormat 是浏览器原生的格式化工具,第一个参数是区域代码,第二个是格式选项。除 de-DE 之外,常用的还有 en-US(10/10/2026, 10:50:45 AM)、fr-FR、ja-JP 等,换一个区域代码即可切换整套日期书写习惯。
效果
同一个模板、同一组数据,只有 Date 域的写法变了:
| 日期域的来源 | 输出 |
|---|---|
DateTime.get_Now().Date.ToString() |
Date: 2026/10/10 上午12:00:00 |
Intl.DateTimeFormat("de-DE", …).format(now) |
Date: 10.10.2026, 10:50:45 |
合并后的文档:
To: John Smith
From: 客户服务部
Subject: 需求确认函
Fax.: +1 (69) 123456 Date: 10.10.2026, 10:50:45
Message:
尊敬的 John Smith:
…

常见问题
合并后文档里还留着 «Contact Name» 这样的尖括号
原因:合并域的占位符 «域名» 是域的结果文本(域代码 MERGEFIELD 与结束标记之间的那段内容)。合并时被替换掉的只有域的结果区——如果模板里的 «、» 是通过在域外面另外输入文字得到的,它们不属于这个域,自然不会被动到。
解决:在 Word 里编辑模板时,先在域位置按 Ctrl + F9 建立域、填好域代码,再按 F9 让 Word 生成结果文本;不要手工在域外敲尖括号。用代码生成模板时,« 与 » 必须写在域的 separate 与 end 标记之间,不能另起一个 run 放在域外。
数组里的域名和模板对不上,对应的位置没填上值
Execute 的两个数组是按下标配对的,且域名匹配区分大小写。"Contact Name" 与 "contact name" 会被当成两个不同的域。
模板里出现、而 fieldNames 中没有列出的域,会被合并成空(结果区变成空字符串,而不是保留原占位符)。这个特性正是「数据没有时自动留白」的实现方式,但如果本意是保留原样,就要把域名补进数组。
域名里带空格,模板里的域代码应该怎么写
在 Word 的域代码中,含空格或其他特殊字符的域名要用双引号括起来:
{ MERGEFIELD "Contact Name" \* MERGEFORMAT }
而在 JavaScript 里传给 Execute 的是域名本身,不加引号:
let fieldNames = ["Contact Name"];
日期格式在不同机器上跑出来不一样
DateTime.get_Now().Date.ToString() 使用运行环境当前的文化设置,因此在不同区域设置的系统、不同浏览器上会得到不同的日期串。要得到稳定输出,就按本文第二个功能点那样,用 Intl.DateTimeFormat 显式指定区域代码后再把结果传进 Execute;也可以先用 Spire 的 DateTime 相关 API 拿到 Date 对象,再用标准的年月日时分秒属性自行拼串。
下载下来的 docx 打不开,提示文件已损坏
原因:Blob 的 MIME 类型写错了,或者保存到 VFS 后没有重新读取就直接下载。
解决:docx 要声明完整的 MIME 类型,并确保下载的是 SaveToFile 之后从 VFS 读出的字节:
const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const modifiedFile = new Blob([modifiedFileArray], {
type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
});
获取免费许可证
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。







