tiptap OrderedList 有序列表扩展完全指南:从 `@tiptap/extension-ordered-list` 到 `@tiptap/extension-list` 的演进与迁移
2026/9/9 21:07:29 网站建设 项目流程

tiptap OrderedList 有序列表扩展完全指南:从@tiptap/extension-ordered-list@tiptap/extension-list的演进与迁移

【免费下载链接】tiptapThe headless rich text editor framework for web artisans.项目地址: https://gitcode.com/GitHub_Trending/ti/tiptap

导读

本文以 tiptap 仓库中 @tiptap/extension-ordered-list 的版本变更日志 为主线,结合 monorepo 中列表扩展的真实实现源码,系统讲解 tiptap 有序列表扩展的架构演进、配置项语义、Schema 与快捷键行为,以及从分散列表包迁移到聚合包@tiptap/extension-list的完整操作步骤。读完本文,你将能理解为何当前extension-ordered-list包是一个"瘦身"后的重导出兼容层,掌握OrderedList全部选项的默认值与底层影响,并能在 v3 时代正确安装、配置和迁移列表相关依赖。

适用前提说明:本文涉及的命令与包结构以当前仓库(v3.30.3 时代、基于 pnpm workspace 的 monorepo)为准。仓库采用 pnpm workspace 管理多包,且各列表扩展已被整合进@tiptap/extension-list@tiptap/extension-ordered-list仅作为兼容性入口存在(见 packages/extension-ordered-list/src/index.ts)。

一、版本日志揭示的事实:一个被"合并"进扩展聚合包的独立扩展

@tiptap/extension-ordered-list的 CHANGELOG.md 记录了该包完整的发布轨迹:从2.0.0-alpha时代、到 v2 稳定期(2.0.x~2.12.0)、再到 v3 时代的3.0.0-beta.x/3.0.0-next.x与最终稳定版 3.30.3。该日志可以提炼出三个关键事实:

  1. v3 稳定版的绝大多数发布都是"Patch Changes",并且几乎全部内容仅是一行依赖说明,例如3.30.3对应@tiptap/extension-list@3.30.3。这说明有序列表的真实实现早已不在本包,而是一路跟随聚合包@tiptap/extension-list的版本号同步发布。
  2. v3.0.0 是一次决定性的架构调整(变更哈希2c911d2):官方将所有列表相关扩展的代码搬进@tiptap/extension-list,本包变成纯转发层,同时引入ListKit作为一次性注册/配置全部列表扩展的推荐方式。
  3. 少数非纯版本号条目揭示出重要的行为变更:v2.11.6 将有序列表默认type值改为null以便于 Schema 扩展(9abb019);beta.26 增加itemTypeName选项(3d7c8e6);2.0.0-beta.219 支持在列表上保留 marks(#3540/#3541,提交36bb1e1)。

1.1 为什么这个包还在,却几乎"没有代码"

对照仓库结构即可验证上述判断。当前 packages/extension-ordered-list 包内:

  • src/index.ts全文仅做三件事:从@tiptap/extension-list导入OrderedList、再导出其类型OrderedListOptions、最后将其作为默认导出——即保持对老式import OrderedList from '@tiptap/extension-ordered-list'的兼容;
  • package.json 中@tiptap/extension-list被声明为唯一的peerDependencies(workspace 内联解析);
  • 真实实现位于 packages/extension-list/src/ordered-list/ordered-list.ts,并在同一包的kit/目录下通过 ListKit 统一装配。

也就是说,对升级到 v3 的用户而言,直接使用聚合包即可拿到与旧包完全等价的OrderedList,同时还能减少对等依赖冲突。

二、v3 官方迁移指南:合并列表包与 ListKit

变更日志在3.0.0/3.0.0-next.6条目中给出了完整的官方迁移说明。官方明确的推荐做法是:使用ListKit一次性配置所有列表扩展

import { ListKit } from "@tiptap/extension-list"; new Editor({ extensions: [ ListKit.configure({ bulletList: { HTMLAttributes: "bullet-list", }, orderedList: { HTMLAttributes: "ordered-list", }, listItem: { HTMLAttributes: "list-item", }, taskList: { HTMLAttributes: "task-list", }, taskItem: { HTMLAttributes: "task-item", }, listKeymap: {}, }), ], });

ListKit的可配置子项与源码完全对应:ListKitOptions 依次暴露bulletListlistItemlistKeymaporderedListtaskItemtaskList六个键,每个键的类型是Partial<选项> | false——即传入false可整体关闭某个子扩展(例如纯文本编辑器不需要任务列表时设taskList: falsetaskItem: false),其余键则透传给对应的.configure()

2.1 依赖清理:卸载旧包、安装聚合包

官方在日志中明确给出了 npm 层面的替换命令。由于代码已全部迁移到聚合包,可移除下列旧依赖:

npm uninstall @tiptap/extension-ordered-list @tiptap/extension-bullet-list @tiptap/extension-list-keymap @tiptap/extension-list-item @tiptap/extension-task-list

再安装聚合包作为替代:

npm install @tiptap/extension-list

值得注意的是:当前仓库本身仍保留着@tiptap/extension-ordered-list这一空壳包,目的就是为上述旧依赖做平滑过渡;若你直接以聚合包为目标,未来升级时就不会再碰到日志3.22.4条目中提到的"依赖更新后触发 peer dependency 解析冲突"(27ea931)这一类连锁问题。

2.2 需要更细粒度控制?也可以单独使用各扩展

官方迁移指南同时指出,如果不想使用聚合式ListKit,也可以单独引入各扩展,以下是日志给出的逐包迁移对照与用法:

OrderedList(本主题核心)

- import OrderedList from '@tiptap/extension-ordered-list' + import { OrderedList } from '@tiptap/extension-list'
import { OrderedList } from "@tiptap/extension-list";

BulletList

- import BulletList from '@tiptap/extension-bullet-list' + import { BulletList } from '@tiptap/extension-list'
import { BulletList } from "@tiptap/extension-list";

ListItem

- import ListItem from '@tiptap/extension-list-item' + import { ListItem } from '@tiptap/extension-list'
import { ListItem } from "@tiptap/extension-list";

TaskList

- import TaskList from '@tiptap/extension-task-list' + import { TaskList } from '@tiptap/extension-list'
import { TaskList } from "@tiptap/extension-list";

TaskItem

- import TaskItem from '@tiptap/extension-task-item' + import { TaskItem } from '@tiptap/extension-list'
import { TaskItem } from "@tiptap/extension-list";

ListKeymap

- import ListKeymap from '@tiptap/extension-list-keymap' + import { ListKeymap } from '@tiptap/extension-list'
import { ListKeymap } from "@tiptap/extension-list";

源码层面,单独的 OrderedList 通过Node.create<OrderedListOptions>({ name: 'orderedList', ... })注册;其 content 定义为${itemTypeName}+,即有序列表内必须含至少一个列表项节点,且官方注释明确:使用 OrderedList 依赖同时启用 ListItem 扩展

三、OrderedList 选项深度解析(结合源码确认默认值)

日志记录的关键演进——新增itemTypeName(beta.26,2021-12-10)、parseHTML属性直接返回值(beta.16,修复 #1863)、保留列表上的 marks(2.0.0-beta.219,#3540/#3541)——最终沉淀为OrderedListOptions的四个选项。其类型定义与默认值可从 ordered-list.ts 的addOptions()与接口注释中完整确认:

选项默认值说明来源/演进
itemTypeName'listItem'列表项节点类型名,用于content声明与切换命令beta.26(3d7c8e6)新增
HTMLAttributes{}渲染到<ol>标签上的自定义 HTML 属性(如class各框架通用
keepMarksfalse拆分列表项时是否保留当前 marks2.0.0-beta.219(#3540/#3541)
keepAttributesfalse拆分列表项时是否保留属性keepMarks配套演进

3.1 keepMarks / keepAttributes 在命令中的真实作用路径

查看 addCommands() 的实现可更准确地理解这两个开关。toggleOrderedList命令在keepAttributes为真时走命令链,先toggleList再用textStyle的属性回填到listItem;否则直接执行commands.toggleList(this.name, this.options.itemTypeName, this.options.keepMarks)

toggleOrderedList: () => ({ commands, chain }) => { if (this.options.keepAttributes) { return chain() .toggleList(this.name, this.options.itemTypeName, this.options.keepMarks) .updateAttributes(ListItemName, this.editor.getAttributes(TextStyleName)) .run() } return commands.toggleList(this.name, this.options.itemTypeName, this.options.keepMarks) },

同理,addInputRules() 会在任一开关为真时改用携带keepMarks/keepAttributes参数及textStyle属性的wrappingInputRule。这解释了日志#3541"Ability to preserve marks on lists"的含义:当用户把带有加粗/斜体等样式的段落转换为有序列表时,这些样式不会丢失。

3.2 HTMLAttributes 的典型用法

HTMLAttributes与 OrderedList 节点自身的start/type属性会被 renderHTML() 通过mergeAttributes(this.options.HTMLAttributes, attributesWithoutType)合并后渲染到<ol>上。例如给所有有序列表加一个用于样式的类:

OrderedList.configure({ HTMLAttributes: { class: 'my-ordered-list' }, })

starttype属于节点内置属性,会被从自定义属性中剥离并按规则输出(见下一节),不会和用户自定义 HTML 属性冲突。

四、Schema 内建属性:start 与 type

有序列表节点在 addAttributes() 中声明了两个属性,理解它们对"粘贴外部富文本"场景尤其重要:

  • start:默认1。解析 HTML 时读取<ol start>,无该属性则回落为1;渲染时仅在start !== 1时输出start属性。
  • type:默认null。解析 HTML 时按三级策略探测编号类型:
    1. 读取<ol>上的type属性;
    2. 读取<ol>style 中的list-style-type(通过cssListStyleTypeToHtmlType映射);
    3. 读取第一个<li>上的list-style-type——官方注释指出这是Google Docs 的典型写法

映射规则集中在源码注释清晰的cssListStyleTypeToHtmlType函数中:

CSSlist-style-type输出的 HTMLtype
upper-romanI
lower-romani
upper-alpha/upper-latinA
lower-alpha/lower-latina
其他值不输出(返回null

渲染时仅当type存在且不等于'1'才输出type属性。这一设计正是日志 v2.11.6 条目"Use null in ordered list's default type value for better schema extension support"(9abb019)的直接体现:把默认值从某种"数字类型"改为null,使parseHTML探测到的type值(如'a''i')能干净地写入 Schema,避免与默认类型混淆,也方便后续扩展覆盖属性。

type相关行为在聚合包测试 orderedListType.spec.ts 中有覆盖;parseHTML特性本身最早由 beta.16 的"属性解析直接返回值"变更(修复 #1863)奠定。

五、快捷键、输入规则与粘贴处理

5.1 快捷键

addKeyboardShortcuts() 为Mod-Shift-7绑定了toggleOrderedList()Mod在 macOS 对应 Cmd、在其他平台对应 Ctrl)。也就是说默认编辑器里按下Ctrl/Cmd + Shift + 7即可切换有序列表。

5.2 输入规则:从"数字加点和空格"开始

有序列表支持"输入即转换",其识别正则在源码中直接导出,便于扩展复用:

export const orderedListInputRegex = /^(\d+)\.\s$/

即当用户输入诸如1.时,wrappingInputRule会命中,并把start属性设为该数字。值得注意的是 joinPredicate 的合并条件:只有当现有列表的type为空或为'1'(未定制编号风格),且现有项数加start等于新输入的数字时,才会把输入合并进已有列表;而像a)i)这类带类型的列表则刻意保持独立,不参与合并。该输入规则能力源自 2.0.0-beta.17 的"把 input rules 与 paste rules 整合进 core"(#1997)。

5.3 纯文本粘贴:识别并重建有序列表结构

源码通过 addProseMirrorPlugins() 注册了一个自定义handlePaste:当剪贴板**只有纯文本(无 HTML)**且能按parsePlainTextOrderedListPaste解析出列表内容时,直接构造orderedList节点并replaceSelectionWith替换选区,从而实现"从外部复制一段带编号的文本粘贴进来自动成为有序列表"。若剪贴板含 HTML 或文本无法解析,则返回false走 ProseMirror 默认路径。

与"误判列表"相关的回归测试存在于 orderedListPhoneNumber.spec.ts——电话号(216) 555-1234这类行中出现的216)不应被识别为列表起点。

5.4 Markdown 协同:数字、字母与罗马数字标记

v3 的列表实现还内置了 Markdown 的解析与渲染:

  • markdownTokenName: 'list'parseMarkdown只处理ordered的 token,并把 token 的starttypeMarker(如aiI)映射为节点属性;
  • renderMarkdown负责把节点内容按换行递归输出;
  • markdownTokenizer使用 utils.ts 与 roman.ts 中的ORDERED_LIST_ITEM_REGEX等工具处理数字/字母/罗马数字多种标记,并支持带缩进的嵌套列表(buildNestedStructure以首项缩进为基准构造层级);
  • markdownOptions.indentsContent: true指示内容需缩进表示层级。

相关测试参见聚合包测试目录 packages/extension-list/tests,其中 listItemMarkdown.spec.ts 与 orderedList 系列测试共同覆盖了这些行为。

六、工程化变更脉络:从 2.x 到 3.x 的关键节点

变更日志中有多条非版本号条目,勾勒出列表包跨越大版本演进的工程化路线:

版本区间变更内容工程含义
3.0.0(Major)a92f4a6:改用 tsup 构建,不再产出 UMD依赖 UMD 产物者需自行重新打包(rollup/esbuild 等)
3.0.0(Major)2c911d2:全部列表包并入@tiptap/extension-list,引入ListKit前文迁移指南的根源
3.0.x 系列1b4c82b:使用 pnpm 包别名做版本锁定;89bd9c7:强制 type-only import 以让打包器在生成 dist/index.js 时忽略类型导入;8c69002:beta 与 stable 功能对齐monorepo 依赖管理、产物体积与稳定性的内部治理
2.11.69abb019:有序列表默认typenullSchema 扩展更友好
2.0.0-beta.21936bb1e1:#3540/#3541,列表保留 marks促成keepMarks/keepAttributes能力
2.0.0-beta.210f387ad3:新增 prosemirror 依赖解析包统一 PM 依赖版本,避免多版本冲突
2.0.0-beta.263d7c8e6:新增itemTypeName选项支持自定义列表项节点名
2.0.0-beta.17723b955:#1997,input rules 与 paste rules 收归 core输入/粘贴能力统一由 core 调度

需要留意的是,日志中 v2 段落在版本号上存在明显的分支/回填痕迹:例如2.11.6之后直接出现2.5.82.5.x等更早的版本,中间穿插大量 "Version bump only" 的占位条目。这属于多发布线合并时常见的补录现象,不影响功能判断。另外 v2 段中 2024-05 的2.4.0条目记录了 "added jsdocs"(b941eea),即从该版本起为扩展补充了完整的 JSDoc 注释——这正是如今OrderedListOptions各字段都带@default/@example注释的由来。

七、当前仓库中的上手资源

若要在真实环境验证上述行为,仓库内已有可直接参考的实现与示例:

  • 有序列表实现全量源码:packages/extension-list/src/ordered-list/ordered-list.ts(选项、属性、命令、快捷键、粘贴、输入规则、Markdown),配套辅助逻辑见同目录 utils.ts 与 roman.ts;
  • ListKit装配逻辑与每个子扩展的开关语义:packages/extension-list/src/kit/index.ts;
  • 聚合包导出入口与各扩展文件索引:packages/extension-list/src/index.ts、packages/extension-list/src/ordered-list/index.ts;
  • 兼容性重导出包(本文主题包):packages/extension-ordered-list/src/index.ts;
  • 行为回归测试:packages/extension-list/tests/orderedListType.spec.ts、packages/extension-list/tests/orderedListPhoneNumber.spec.ts、packages/extension-list/tests/listItemMarkdown.spec.ts、packages/extension-list/tests/listKeymapTab.spec.ts;
  • 可运行的前端示例(Vue/React/JSX 三套入口):demos/src/Nodes/OrderedList。

八、小结:升级到 v3 时的三句话结论

  • 依赖上:用@tiptap/extension-list取代@tiptap/extension-ordered-list等六个旧包,并按需通过ListKit.configure({ orderedList: { ... } })统一配置;老包作为兼容层仍可在升级过渡期使用。
  • 配置上itemTypeNameHTMLAttributeskeepMarkskeepAttributes四个选项的默认值与行为全部可在 ordered-list.ts 中追溯,start/type属性负责承载编号起点与罗马/字母编号风格。
  • 行为上Ctrl/Cmd+Shift+71.输入转换、纯文本粘贴解析、Markdown 双向转换都已内建;若需要 UMD 构建产物,则需自行使用打包器重新封装(v3 已移除 UMD 输出)。

【免费下载链接】tiptapThe headless rich text editor framework for web artisans.项目地址: https://gitcode.com/GitHub_Trending/ti/tiptap

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询