docx 库字段(Fields)完全指南:SimpleField、公式字段与邮件合并域实战
【免费下载链接】docxEasily generate and modify .docx files with JS/TS with a nice declarative API. Works for Node and on the Browser.项目地址: https://gitcode.com/GitHub_Trending/do/docx
字段(Fields)是 Word 文档中一段可以随环境或数据动态变化的文本:页码、作者名、保存日期、文档属性乃至整个目录都可以通过字段实现。本文以 docx 库官方文档 docs/usage/fields.md 为骨架,结合仓库源码与 demo/66-fields.ts 完整示例,讲解如何用SimpleField插入字段代码、如何借助缓存值(cached value)控制初始显示、如何用书签变量编写公式字段,以及如何用SimpleMailMergeField为模板文档接入邮件合并。读完你即可在 Node.js 或浏览器环境下生成包含动态文本的.docx文档。
什么是字段(Fields)
字段是一段动态文本,插入后内容由 Word(或其他兼容的文字处理器)在打开文档、打印或按 F9 时根据字段代码(field code)重新计算。最常用的字段是页码与交叉引用,也可以插入文档属性,例如作者名(AUTHOR)或最后保存日期。
docx 库把字段封装成了常规的段落子元素,你可以像添加TextRun一样把它放进Paragraph的children数组中,无需手动拼接任何 OOXML 标签。
字段代码速查表
Word 使用字段代码来标识字段的计算结果。你可以在 Word 中通过Insert -> Quick Parts -> Field...插入一个字段,再点击 "Field codes" 按钮查看对应的字段代码。以下是常用字段代码及其含义:
| 字段类型 | 示例 | 说明 |
|---|---|---|
| = (Formula) | =2*21 | 计算公式的结果,也可以使用书签作为变量(见下文"公式字段")。 |
| Author | AUTHOR | 显示文档属性中记录的作者。 |
| CreateDate | CREATEDATE | 文档的创建日期。 |
| Date | DATE | 今天的日期。 |
| FileName | FILENAME \p | 文档名称,追加\p开关可显示完整路径。 |
| Info | INFO NumWords | 文档属性中的数据,例如文档的总字数。 |
| NumPages | NUMPAGES | 文档的总页数。 |
| UserName | USERNAME | Office 个性化设置中的用户名。 |
补充:字段代码不区分大小写,
AUTHOR、author与Author等价;文档创建日期同时存在对应的底层指令CREATEDATE,配合格式开关可控制显示样式,例如 demo 中的CREATEDATE \@ "d MMMM yyyy"会渲染成类似 "16 September 2026" 的格式。
简单字段(SimpleField)
Word 中有些字段非常复杂(比如目录 TOC),但在很多场景下,整个字段只需要一段指令文本、拥有相同的格式属性即可。此时可以使用简单字段。
在 OOXML 中,简单字段对应w:fldSimple元素,字段指令放在w:instr属性中。docx 库的SimpleField类即封装了这一结构,源码位于 src/file/paragraph/run/simple-field.ts:构造时以instruction为w:instr属性值创建FldSimpleAttrs,若传入了缓存值则追加一个TextRun作为子元素。
字段可以作为一个段落的孩子添加:
import { Document, Packer, Paragraph, SimpleField, TextRun } from "docx"; const paragraph = new Paragraph({ children: [new TextRun("This document was created by: "), new SimpleField("AUTHOR")], });缓存值(cached value)
字段可以包含一个缓存值(cached value),用来在没有重新计算全部字段的情况下,让文字处理器先展示一段文本。缓存值可以在打开文档后通过选中字段并按下 F9 更新。缓存值作为构造函数第二个参数传入:
const paragraph = new Paragraph({ children: [new TextRun("This document was created by: "), new SimpleField("AUTHOR", "Richard Brodie")], });从实现上看,缓存值会被渲染成w:fldSimple内部的TextRun(见 src/file/paragraph/run/simple-field.ts)。这正是文档"先有可读内容、再按需刷新"的关键机制——例如在 demo/66-fields.ts 中new SimpleField("NUMWORDS", "34")先显示 "34" 作为占位,待 Word 计算后更新为真实字数。
公式字段(Formulas)
公式是字段的一种,可以用来做基础计算。计算既可以使用静态值(例如=2*21),也可以引用书签中的值。下面这个例子演示了如何把两个书签的值相加:
import { Bookmark, Paragraph, SimpleField, TextRun } from "docx"; const paragraph = new Paragraph({ children: [ new TextRun("Value one is: "), new Bookmark({ id: "One", children: [new TextRun("451")] }), new TextRun(". The second value is: "), new Bookmark({ id: "Two", children: [new TextRun("886")] }), new TextRun(". The sum of these values is: "), new SimpleField("=One+Two"), ], });这里的诀窍是:Bookmark中的文本即书签的值,而=One+Two指令中的One、Two对应书签 id。demo 中还有更进阶的用法,把公式与书签结合描述一个打印场景:
new Bookmark({ id: "TimesPrinted", children: [new TextRun("42")], }), // ... new SimpleField("=INT((TimesPrinted+1)/2)"),即"若打印 42 次双面,需要INT((42+1)/2)张纸",公式会引用书签值自动计算。书签的完整用法可参考 docs/usage/bookmarks.md。
邮件合并字段(Mail merge fields)
字段在邮件合并(mail merge)中非常常见:先创建模板文档,再在合并阶段把来自 Excel 或数据库的数据插入文档。
docx 库为此提供了便捷类SimpleMailMergeField,只需传入数据集中的字段名即可,与普通字段一样作为段落子元素使用:
const paragraph = new Paragraph({ children: [new TextRun("Your score was "), new SimpleMailMergeField("Score"), new TextRun(" of 100 points")], });这段代码与下面的写法完全等价:
const paragraph = new Paragraph({ children: [new TextRun("Your score was "), new SimpleField("MERGEFIELD Score", "«Score»"), new TextRun(" of 100 points")], });从源码看(src/file/paragraph/run/simple-field.ts),SimpleMailMergeField直接继承SimpleField,构造时生成指令MERGEFIELD ${fieldName}并把«${fieldName}»作为缓存值——« »是 Word 邮件合并的默认占位符样式,合并执行后会被真实数据替换。
简单字段与复杂字段:底层结构差异
理解fldSimple之前,需要知道 Word 还有另一套"复杂字段"表示法。复杂字段由三个字段字符(w:fldChar)围出区域:begin标记开始、separate分隔字段指令与结果、end标记结束。docx 库在 src/file/paragraph/run/field.ts 中提供了createBegin、createSeparate、createEnd三个工厂函数,并支持dirty属性(标记字段需要重新计算)。
// 一个复杂字段的 OOXML 结构示意 // <w:r><w:fldChar w:fldCharType="begin" w:dirty="true"/></w:r> // <w:r><w:instrText>PAGE</w:instrText></w:r> // <w:r><w:fldChar w:fldCharType="separate"/></w:r> // <w:r><w:fldChar w:fldCharType="end"/></w:r>与之相对,简单字段w:fldSimple把指令与结果收敛在单个元素内,不需要 begin/end 配对,因而更适合"指令简单、无需嵌套"的场景。需要嵌套结构(如指令文本内部还要区分格式)的字段,则必须走复杂字段路线。
仓库内置的其他字段能力(源码佐证)
除了文档正文提到的SimpleField与SimpleMailMergeField,docx 库还针对常见字段场景提供了开箱即用的封装类,可以在段落中直接使用:
- 页码与节信息:
src/file/paragraph/run/page-number.ts提供了Page(PAGE指令,当前页号)、NumberOfPages(NUMPAGES,总页数)、NumberOfPagesSection(SECTIONPAGES,本节页数)、CurrentSection(SECTION,当前节号)。这些指令以w:instrText输出,通常与复杂字段的 begin/end 配合使用。需要页码的更多用法可参考 docs/usage/page-numbers.md。 - 顺序编号(SEQ):
src/file/paragraph/run/sequential-identifier.ts中的SequentialIdentifier生成SEQ字段,可分别为图、表、公式维护独立的自动递增序列,例如new SequentialIdentifier("Figure")。它内部组合了createBegin(true)+SEQ指令 +createSeparate()+createEnd(),属于复杂字段的典型封装。 - 页码交叉引用(PAGEREF):
src/file/paragraph/links/pageref.ts中的PageReference(bookmarkId, options)生成PAGEREF字段,用于显示某书签所在页码,支持hyperlink(\h开关,将引用变为超链接)与useRelativePosition(\p开关,同页时显示 "above/below"、跨页时显示 "on page #")。 - 编号项引用(REF):
src/file/paragraph/links/numbered-item-ref.ts中的NumberedItemReference基于SimpleField实现REF字段,可引用书签对应段落的编号文本,并通过NumberedItemReferenceFormat(none/relative(\r) /no_context(\n) /full_context(\w))控制编号的展示粒度,默认hyperlink: true、full_context。 - 目录(TOC):目录本质上也是一个复杂字段。
src/file/table-of-contents/field-instruction.ts会把配置项(如headingStyleRange、hyperlink、stylesWithLevels)拼装成TOC \o "1-3" \h形式的指令文本,详细用法见 docs/usage/table-of-contents.md。
完整示例:一个充满字段的文档
仓库中的 demo/66-fields.ts 把上述知识点串成了一个可直接运行的完整示例——它创建了一个包含文件名、创建日期、作者、字数、书签和公式字段的文档,并导出为 "My Document.docx":
// Use fields to include dynamic text import * as fs from "fs"; import { Bookmark, Document, Packer, Paragraph, SimpleField, TextRun } from "docx"; const doc = new Document({ creator: "Me", sections: [ { properties: {}, children: [ new Paragraph({ children: [ new TextRun("This document is called "), new SimpleField("FILENAME", "My Document.docx"), new TextRun(", was created on "), new SimpleField('CREATEDATE \\@ "d MMMM yyyy"'), new TextRun(" by "), new SimpleField("AUTHOR"), ], }), new Paragraph({ children: [ new TextRun("The document has "), new SimpleField("NUMWORDS", "34"), new TextRun(" words and if you'd print it "), new Bookmark({ id: "TimesPrinted", children: [new TextRun("42")], }), new TextRun(" times two-sided, you would need "), new SimpleField("=INT((TimesPrinted+1)/2)"), new TextRun(" sheets of paper."), ], }), ], }, ], }); Packer.toBuffer(doc).then((buffer) => { fs.writeFileSync("My Document.docx", buffer); });运行方式:在仓库根目录(或你自己的项目里安装docx后)用 ts-node / tsx 执行该脚本,即可在输出目录得到生成的.docx文件。示例中所有SimpleField都提供了缓存值,因此文档在没有打开 Word 重新计算字段前也能正常显示一段合理的内容。
实战注意事项
- 缓存值决定了未刷新前的显示:给
SimpleField传入第二个参数可以保证文档在计算前就具备可读内容;不传则字段区域可能为空。对于MERGEFIELD这类字段,缓存值«Score»还是最终数据的占位提示。 - F9 刷新:Word 打开文档后默认不会自动重算全部字段,选中字段按 F9 即可更新;打印时 Word 通常也会刷新部分字段。
- 复杂字段是兜底方案:当字段需要跨多个 run 分隔、或者指令本身需要细分格式(例如 src/file/paragraph/run/page-number.ts 中的指令输出)时,应使用
createBegin/createSeparate/createEnd构造复杂字段,而不是SimpleField。 - 指令字符串保持原样:
SimpleField的instruction参数会原样写入w:instr,因此开关、空格与格式开关(如\@ "d MMMM yyyy"、\p)都需要你自己拼写正确;拼写错误不会被库拦截,只会影响 Word 侧的计算结果。
小结
字段是让 docx 文档"活起来"的关键机制。通过SimpleField一行代码即可注入 AUTHOR、DATE、NUMPAGES 等动态内容;借助缓存值参数可以控制刷新前的显示;=公式字段配合Bookmark能完成引用书签值的计算;SimpleMailMergeField则让模板与外部数据源的合并变得轻而易举。若需要更复杂的页码、交叉引用或目录场景,仓库还提供了PageReference、NumberedItemReference、SequentialIdentifier与TableOfContents等内置封装,它们都是建立在本文所述的字段机制之上的。
【免费下载链接】docxEasily generate and modify .docx files with JS/TS with a nice declarative API. Works for Node and on the Browser.项目地址: https://gitcode.com/GitHub_Trending/do/docx
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考