docx 实战指南:用bullet属性在 JS/TS 中快速生成 Word 项目符号列表
【免费下载链接】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
本文聚焦 docx 库中项目符号列表(Bullet Points)的完整用法:从最基础的bullet: { level: 0 }段落配置,到多级嵌套列表、程序化批量生成、与其他段落混排以及富文本样式定制,并深入仓库源码剖析 bullet 在 OOXML 层(w:numPr/w:ilvl/w:numId)的真实映射原理。读完本文,你将能独立用 docx 在 Node 或浏览器环境中生成任意结构的 Word 无序列表。
说明:Bullet Points 的实现依赖 Paragraph(段落)概念,建议先阅读 Paragraph 文档 建立基础。
基础用法:为段落添加bullet属性
在 docx 中,项目符号列表并不需要单独的数据结构,只需在Paragraph的配置对象中加上bullet属性即可。bullet是一个对象,目前只接受一个必填字段level(缩进层级):
import { Document, Paragraph } from "docx"; const doc = new Document({ sections: [ { children: [ new Paragraph({ text: "First item", bullet: { level: 0, }, }), new Paragraph({ text: "Second item", bullet: { level: 0, }, }), new Paragraph({ text: "Third item", bullet: { level: 0, }, }), ], }, ], });生成效果(Word 中渲染为圆点列表):
- First item
- Second item
- Third item
在 段落属性源码 中可以看到,bullet的类型定义极为精简:
readonly bullet?: { /** Indentation level for the bullet (0-8) */ readonly level: number; };level唯一决定了项目符号的层级与缩进位置,这正是 docx 声明式 API 的特点——用最少配置表达常见需求。
多级列表:用不同level值构建嵌套结构
Word 支持最多 9 个列表层级(level取 0–9),通过给不同段落指定不同level,即可天然形成父子嵌套关系,无需手动管理缩进:
const doc = new Document({ sections: [ { children: [ new Paragraph({ text: "Main item 1", bullet: { level: 0 } }), new Paragraph({ text: "Sub-item 1.1", bullet: { level: 1 } }), new Paragraph({ text: "Sub-item 1.2", bullet: { level: 1 } }), new Paragraph({ text: "Deep item 1.2.1", bullet: { level: 2 } }), new Paragraph({ text: "Main item 2", bullet: { level: 0 } }), ], }, ], });渲染效果:
- Main item 1
- Sub-item 1.1
- Sub-item 1.2
- Deep item 1.2.1
- Main item 2
值得注意的是,docx 为bullet模式内置了完整的默认符号与缩进方案。在 numbering.ts 源码 中,Document初始化时会自动注册一个名为default-bullet-numbering的抽象编号定义,共配置 0–8 共 9 个级别,各级符号与缩进如下(来自仓库内真实配置):
| level | 符号(Unicode) | 左缩进 | 悬挂缩进 |
|---|---|---|---|
| 0 | \u25CF● | 0.5 in | 0.25 in |
| 1 | \u25CB○ | 1 in | 0.25 in |
| 2 | \u25A0■ | 2160 twip | 0.25 in |
| 3 | \u25CF● | 2880 twip | 0.25 in |
| 4 | \u25CB○ | 3600 twip | 0.25 in |
| 5 | \u25A0■ | 4320 twip | 0.25 in |
| 6 | \u25CF● | 5040 twip | 0.25 in |
| 7 | \u25CF● | 5760 twip | 0.25 in |
| 8 | \u25CF● | 6480 twip | 0.25 in |
符号在 ● / ○ / ■ 之间循环,缩进随层级递增(0.5 英寸起步,之后每级约增加 720 twip,1 twip = 1/20 磅)。也就是说,即使用户不配置任何编号方案,bullet也能直接产出规范的多级列表外观。
bullet选项参数速查
原文档给出bullet对象的唯一配置项:
| Property | Type | Notes | Description |
|---|---|---|---|
| level | number | Required | Indentation level (0-9) |
结合源码补充两个关键细节:
- 自动应用列表段落样式:当段落设置了
bullet时,properties.ts 会自动为该段落注入ListParagraph段落样式,保证列表段落与正文段落有正确的行距与缩进基调; - 层级上限校验:在 unordered-list.ts 源码 中,
IndentLevel构造器会对level > 9抛出错误(Level cannot be greater than 9),这与 Word 本身最多支持 9 级列表的限制一致;内置默认配置实际只定义到 8 级,超出后如需自定义符号样式,应改用numbering配置(见后文“相关主题”)。
程序化列表生成:从数组批量生成项目符号
实际项目中列表数据通常来自接口或配置,可以用Array.prototype.map一行生成多个 bullet 段落:
const items = ["Apple", "Banana", "Cherry", "Date"]; const doc = new Document({ sections: [ { children: items.map( (item) => new Paragraph({ text: item, bullet: { level: 0 }, }), ), }, ], });渲染效果:
- Apple
- Banana
- Cherry
- Date
这种写法把“数据 → 段落”的转换完全函数化,适合搭配任意数据源(CSV、数据库查询结果、API 响应等)动态构建文档。
嵌套数据结构:递归生成层级列表
当数据本身具有树形结构(如菜单、目录、分类树)时,可以编写一个递归函数,每深入一层level + 1,即可自动映射为 Word 多级列表:
interface MenuItem { name: string; children?: MenuItem[]; } const menu: MenuItem[] = [ { name: "Fruits", children: [{ name: "Apple" }, { name: "Orange" }], }, { name: "Vegetables", children: [{ name: "Carrot" }, { name: "Broccoli" }], }, ]; function createBulletItems(items: MenuItem[], level: number = 0): Paragraph[] { const paragraphs: Paragraph[] = []; for (const item of items) { paragraphs.push( new Paragraph({ text: item.name, bullet: { level }, }), ); if (item.children) { paragraphs.push(...createBulletItems(item.children, level + 1)); } } return paragraphs; } const doc = new Document({ sections: [ { children: createBulletItems(menu), }, ], });渲染效果:
- Fruits
- Apple
- Orange
- Vegetables
- Carrot
- Broccoli
注意递归函数返回的是扁平化的Paragraph[]数组,最终由 docx 根据level重建层级关系——这种“数据扁平、层级由 level 表达”的设计,让生成逻辑非常干净。
混合内容:Bullet 与标题、普通段落共存
文档中列表很少单独存在,通常与标题、说明文字穿插。docx 允许在同一个 section 的children数组里自由混排:
import { HeadingLevel } from "docx"; const doc = new Document({ sections: [ { children: [ new Paragraph({ text: "Shopping List", heading: HeadingLevel.HEADING_1, }), new Paragraph("Items to buy:"), new Paragraph({ text: "Milk", bullet: { level: 0 } }), new Paragraph({ text: "Bread", bullet: { level: 0 } }), new Paragraph({ text: "Eggs", bullet: { level: 0 } }), new Paragraph("Remember to check expiration dates!"), ], }, ], });渲染效果:
Shopping List
Items to buy:
- Milk
- Bread
- Eggs
Remember to check expiration dates!
由于列表与正文本质上都是Paragraph,混排时无需任何特殊处理;bullet只会作用于设置了该属性的段落,前后普通段落不受影响。
富文本列表项:对 bullet 段落应用文本样式
bullet只负责列表外观,列表项内部的文本格式由TextRun控制。将bullet与children: TextRun[]组合,即可实现“项目符号 + 富文本内容”:
import { TextRun } from "docx"; new Paragraph({ bullet: { level: 0 }, children: [ new TextRun({ text: "Important: ", bold: true }), new TextRun("This item requires attention"), ], });渲染效果:
- Important:This item requires attention
TextRun支持 docx 提供的全部 run 级格式(加粗、斜体、字体、颜色、下划线等),这意味着列表项可以携带任意复杂的内联排版,例如在列表中嵌入超链接或特殊字符。
源码透视:bullet在 OOXML 中的真实映射
理解底层机制有助于排查自定义列表样式的问题。当段落设置bullet后,properties.ts 会调用:
if (options.bullet) { this.push(new NumberProperties(1, options.bullet.level)); }即固定以numId = 1引用内置的default-bullet-numbering定义。NumberProperties的实现位于 unordered-list.ts,最终产出如下 OOXML 结构:
<w:numPr> <w:ilvl w:val="0"/> <w:numId w:val="1"/> </w:numPr>w:numPr:段落的编号属性容器;w:ilvl:列表层级(与bullet.level对应);w:numId:编号实例 ID,bullet模式固定为1,指向内置的默认项目符号编号定义。
该结构的正确性有单元测试兜底,见 unordered-list.spec.ts:测试断言new NumberProperties(5, 9)会序列化为w:numPr下包含w:ilvl(val=9)与w:numId(val=5),并验证level > 9时构造函数抛出异常。
对于需要完全自定义符号(如使用特殊字符、图片符号、自定义缩进)的场景,应放弃bullet快捷属性,改用numbering配置——在Document上定义numbering.config,配合LevelFormat.BULLET与text字段实现,仓库中的 numbering.ts 源码 给出了自定义 bullet level 的典型写法。
完整可运行示例
仓库提供了同时演示编号列表(罗马数字、自定义符号)与多级 bullet 的完整脚本:demo/3-numbering-and-bullet-points.ts。该示例展示了:
- 使用
numbering.config定义两套自定义方案(my-crazy-numbering与my-unique-bullet-points); - 在同一 section 中混用
bullet快捷属性与numbering引用(二者可自由切换,层级各自独立); - 在页眉
Header与页脚Footer中同样可以使用列表段落; - 自定义 bullet 通过
LevelFormat.BULLET配合任意 Unicode 字符(\u1F60、\u00A5、\u273F、\u267A、\u2603)实现个性化符号,并借助convertInchesToTwip精确控制各级缩进; - 最后通过
Packer.toBuffer(doc)导出为My Document.docx。
相关主题
- Numbering(编号列表与自定义编号方案) —— 需要自定义列表符号、编号格式(罗马数字、字母等)或重启编号时使用;
- Paragraph(段落格式) —— 理解段落属性、
TextRun与对齐、缩进等基础; - Styling with JS(JS 样式定制) —— 通过样式系统为列表定义可复用的样式模板。
【免费下载链接】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),仅供参考