ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

CKEditor 5 typing 功能深度解析:输入与删除管道、撤销粒度与自动文本转换

2026/9/16 9:31:17 拓冰建站 浏览量
CKEditor 5 typing 功能深度解析:输入与删除管道、撤销粒度与自动文本转换 CKEditor 5 typing 功能深度解析输入与删除管道、撤销粒度与自动文本转换【免费下载链接】ckeditor5Powerful rich text editor framework with a modular architecture, modern integrations, and features like collaborative editing.项目地址: https://gitcode.com/GitHub_Trending/ck/ckeditor5导读CKEditor 5 的ckeditor/ckeditor5-typing包是整个编辑器书写体验的基石它负责处理键盘输入、内容删除以及输入法组合composition同时内置了自动文本转换autocorrect能力让(c)自动变成©、1/2自动变成½。本文将围绕该包的 API 文档与仓库源码从胶水插件 Typing的架构出发深入剖析输入管道、删除管道、撤销粒度配置并给出自动文本转换的完整配置示例与底层实现原理。一、功能总览typing 包在编辑器中扮演什么角色根据 typing.md 的说明ckeditor/ckeditor5-typing包实现两大能力文本输入与删除——处理用户的键入inputting与删除deleting操作自动文本转换autocorrect——把预定义片段自动转成更美观、更规范的形态。在架构上Typing插件是一个典型的胶水插件glue plugin。查看 src/typing.ts 可以确认它自身不实现任何逻辑而是通过requires声明加载两个核心插件export class Typing extends Plugin { public static get requires(): PluginDependenciesOf[ Input, Delete ] { return [ Input, Delete ]; } // ... }Input见 src/input.ts处理来自键盘或其他输入法的文本输入Delete见 src/delete.ts处理Delete、Backspace等删除操作。通常由 Essentials 自动启用绝大多数编辑器预设无需手动注册Typing。查看 packages/ckeditor5-essentials/src/essentials.ts 可以看到Essentials插件的依赖列表中已经包含Typing与 Clipboard、Enter、SelectAll、ShiftEnter、Undo 等基础功能并列。也就是说只要你在插件列表中启用了Essentials输入、删除与撤销的基本能力就会一并就绪。二、安装与引入该包是 CKEditor 5 开源聚合包的一部分通过安装聚合包即可使用npm install ckeditor5随后在构建配置中引入所需插件import { ClassicEditor, Essentials, Typing, TextTransformation } from ckeditor5; ClassicEditor .create( document.querySelector( #editor ), { licenseKey: YOUR_LICENSE_KEY, // 或 GPL plugins: [ Essentials, TextTransformation, /* ... */ ], typing: { // 输入/删除与文本转换配置 } } ) .then( /* ... */ ) .catch( /* ... */ );注意Typing通常随Essentials自动加载无需显式添加而TextTransformation是一个独立插件需要按需加入plugins数组详见下文第五节。三、输入管道Input 插件如何把字符送进编辑器Input插件src/input.ts的职责是处理来自键盘或其他输入法的文本输入。它的实现非常值得研究以下是几个关键环节。3.1 观察器与命令注册在init()中插件完成两件基础工作src/input.tsview.addObserver( InsertTextObserver ); const insertTextCommand new InsertTextCommand( editor, editor.config.get( typing.undoStep ) || 20 ); editor.commands.add( insertText, insertTextCommand ); editor.commands.add( input, insertTextCommand ); // 向后兼容的别名InsertTextObserversrc/inserttextobserver.ts负责监听视图层insertText、beforeinput等事件把浏览器的原生输入行为翻译成编辑器可感知的事件同时注册insertText命令并保留input别名其底层实现见 src/inserttextcommand.ts。3.2 基于 beforeinput 的输入队列Input插件的核心机制是TypingQueue输入队列。由于浏览器在beforeinput事件触发时尚未真正修改 DOM编辑器不能立即把字符写入模型而是需要等浏览器先把 DOM 改了再验证并同步到模型。从源码可以看到以下事件处理链src/input.ts监听到beforeinput时以high优先级冲刷上一次排队的内容监听到insertText事件时把文本与选区以ModelLiveRange形式存储防止模型变化后选区失效压入队列通过MutationObserver检测到相关 DOM 变化mutations事件时冲刷队列把insertText真正执行到模型内置 50ms 的防抖冲刷作为兜底防止突变观察器未及时触发的极端情况flushDebounced见 src/input.ts。这种设计保证了浏览器 DOM、编辑器视图与模型三者始终一致尤其是在 Safari 等对非组合事件处理有特殊行为的浏览器中尤为重要。3.3 输入法组合composition与 Android 特判中文、日文等输入法依赖 IME 组合composition事件。Input插件做了细致的处理在compositionstart时如果当前选区非折叠先通过model.deleteContent()删除选中内容防止组合输入覆盖已选中文本src/input.ts在compositionend时以high优先级冲刷队列确保所有组合字符在组合结束前写入模型再以lowest优先级触发组合后修复清理被忽略的 DOM 变化如 NBSP 与普通空格的差异src/input.ts在 Android 上英文输入也会触发组合事件因此插件会对比目标范围内的已有文本与待插入文本只插入差异部分env.isAndroid分支src/input.ts避免整词重复插入。3.4 InsertTextCommand 的执行语义InsertTextCommand.execute()src/inserttextcommand.ts遵循先删后插的两步式替换取出选区上原有的格式属性如加粗、链接保证替换后格式不丢失model.deleteContent( selection )删除旧内容model.insertContent( ... )以保留的属性插入新文本。命令还支持text、selection、range、resultRange四个参数src/inserttextcommand.ts其中selection与range二选一resultRange用于控制插入后光标落点。四、删除管道Delete 插件与删除命令Delete插件src/delete.ts处理Delete、Backspace以及其他导致内容删除的用户动作。4.1 方向与命令注册DeleteCommand在创建时需要指定方向src/delete.tsforward向前即Delete→ 注册为deleteForward命令别名forwardDelete向后兼容backward向后即Backspace→ 注册为delete命令。4.2 删除的单位与序列DeleteCommand.execute()支持三个关键选项src/deletecommand.ts选项说明unit删除粒度character、codePoint、word默认为按字符删除sequence长按按键时第几次触发删除事件未松键默认1selection要删除的选区默认使用当前模型选区执行逻辑src/deletecommand.ts若选区折叠先按指定方向调用model.modifySelection()扩展选区启用treatEmojiAsSingleUnit把 emoji 当作单一单位处理计算被删除内容的原子变更数并通过TypingChangeBuffer累计到当前撤销批次详见第六节调用model.deleteContent()完成删除。4.3 空编辑器与空块的特殊处理源码中针对两个易出错场景做了兜底空编辑器中按下删除若当前 limit 元素为空且允许段落存在则把整个内容替换为一个空段落src/deletecommand.ts避免编辑器陷入无内容可选的状态在首块空块中按 Backspace若空块是 limit 元素的第一个子元素则将其替换为段落src/deletecommand.ts。4.4 组合输入期间的删除与撤销联动在组合composition进行中浏览器会直接修改 DOM渲染器被禁用此时Delete插件不拦截默认行为而是交由浏览器处理src/delete.tsDelete插件暴露了requestUndoOnBackspace()方法若下一次用户动作是 Backspace则撤销上一次变更。该方法被TextTransformation在完成自动转换后调用——这正是把(c)转成©后按退格键能一次性撤销整个转换的实现基础src/delete.ts、src/texttransformation.ts。五、自动文本转换TextTransformation / autocorrect这是 typing 包中最贴近用户、最可配置的功能。官方特性文档位于 packages/ckeditor5-typing/docs/features/text-transformation.md仓库还提供了可直接体验的交互演示text-transformation.html 与扩展演示 text-transformation-extended.html。5.1 开箱即用的默认转换输入以下片段会立刻被替换为更美观的形式输入输出(tm)™1/2½-→--–foo“foo”5.2 预定义转换的完整清单按组根据 src/typingconfig.ts 的文档说明特性默认通过transformations.include启用以下四组转换排版组typographyellipsis...→…enDash--→–emDash---→—引号组quotesquotesPrimaryFoo bar→“Foo bar”quotesSecondaryFoo bar→‘Foo bar’符号组symbolstrademark(tm)→™registeredTrademark(r)→®copyright(c)→©数学组mathematicaloneHalf1/2→½oneThird1/3→⅓twoThirds2/3→⅔oneForth1/4→¼threeQuarters3/4→¾lessThanOrEqual→≤greaterThanOrEqual→≥notEqual!→≠arrowLeft-→←arrowRight-→→其他未归组但可直接按名引用quotesPrimaryEnGbFoo bar→‘Foo bar’quotesSecondaryEnGbFoo bar→“Foo bar”quotesPrimaryPlFoo bar→„Foo bar”quotesSecondaryPlFoo bar→‚Foo bar’这些定义的源码实现可以在 src/texttransformation.ts 中找到其中组与成员的关系由TRANSFORMATION_GROUPS表维护src/texttransformation.ts。5.3 配置项include、remove、extraTextTransformationConfig提供三个互操作的配置项src/typingconfig.tsinclude完全覆盖默认列表。可以引用上面的组名、转换名或直接书写自定义规则remove从 include 与 extra 合并后的列表中移除指定项extra在既有列表基础上追加自定义转换。三者的优先级逻辑在normalizeTransformations()中实现src/texttransformation.ts先把include与extra合并再过滤掉remove命中的项最后展开组名并去重。值得注意的一个细节是字符串形式的规则若在预定义表中找不到会被静默过滤掉见 src/texttransformation.ts所以拼写组名/转换名时要格外小心。示例一使用include只保留指定组并追加自定义规则ClassicEditor .create( { // ... 其他配置 ... typing: { transformations: { include: [ // 只使用 quotes 与 typography 两组。 quotes, typography, // 再加上一条自定义转换。 { from: CKE, to: CKEditor } ], } } } ) .then( /* ... */ ) .catch( /* ... */ );示例二使用remove与extra精确裁剪并扩展ClassicEditor .create( { // ... 其他配置 ... typing: { transformations: { remove: [ // 不用 symbols 与 quotes 两组。 symbols, quotes, // 也不要用这两条。 arrowLeft, arrowRight ], extra: [ // 自定义 emoji 转换。 { from: :), to: }, { from: :1:, to: }, { from: :tada:, to: }, // 正则模式规则必须用 $ 结尾且所有片段都要用捕获组包裹。 // 下面这条把 foo 变成 «foo»。 { from: /(^|\s)()([^]*)()$/, to: [ null, «, null, » ] }, // to 还可以是回调函数把句号/问号/感叹号后的首字母自动大写。 { from: /([.?!] )([a-z])$/, to: matches [ null, matches[ 1 ].toUpperCase() ] } ], } } } ) .then( /* ... */ ) .catch( /* ... */ );5.4 规则格式TextTypingTransformationDescription一条转换规则由from和to两个字段构成src/typingconfig.ts。from字符串或正则字符串直接检查输入结尾是否与之匹配内部会被转义并包装为(from)$的正则见normalizeFrom()src/texttransformation.ts正则整个正则必须全部由捕获组覆盖并且必须以$结尾因为它是与输入结尾做比较的。to字符串、数组或函数字符串原样替换但只适用于from也是字符串的情况数组长度必须与from正则的捕获组数量一致null表示该捕获组原样保留、不替换函数接收正则匹配数组作为参数返回上述数组可用于实现大小写转换等动态逻辑。to在内部会被规范化为输入匹配数组、输出替换数组的函数见normalizeTo()src/texttransformation.ts。5.5 底层执行流程与边界从 src/texttransformation.ts 可以还原完整的运行机制动态开关插件监听模型选区变化当光标位于代码块codeBlock或行内代码code属性内时自动禁用自身避免误转换代码内容文本监听创建TextWatchersrc/textwatcher.ts监听键入与选区事件通过testCallback逐个测试规范化后的转换规则触发替换命中matched:data事件且当前批次属于键入操作batch.isTyping时按捕获组把匹配文本替换为规则指定的内容并继承原位置上的文本格式属性如加粗撤销联动替换完成后调用deletePlugin.requestUndoOnBackspace()让一次退格即可撤销整个自动转换。六、撤销粒度typing.undoStep 配置输入与删除的变更如何分组、何时产生一个可撤销步骤答案在TypingChangeBuffersrc/utils/changebuffer.ts中。每个**批次batch**对应一个撤销步骤TypingChangeBuffer会把连续的小变更累积到同一批次里当累积的原子变更数超过limit时自动开启新批次limit即配置项typing.undoStep默认值为20——大致意思是每输入或删除约 20 个字符产生一个新的撤销步骤。undoStep在Input插件初始化时被读取src/input.tsDeleteCommand构造函数同样读取该配置src/deletecommand.ts。配置示例ClassicEditor .create( { typing: { // 值越小撤销粒度越细撤销步数越多值越大一次撤销删除/输入的内容越多。 undoStep: 50 } } ) .then( /* ... */ ) .catch( /* ... */ );七、TypingConfig 配置参考TypingConfig是 typing 各特性的统一配置入口src/typingconfig.ts完整字段如下配置项类型默认值说明typing.undoStepnumber20输入/删除的撤销粒度约每累计 N 个字符产生一个新撤销步骤typing.transformationsTextTransformationConfig四组预定义转换自动文本转换配置include/remove/extra八、相关能力与验证途径可运行演示特性文档对应的交互演示位于 docs/_snippets/features/text-transformation.html、text-transformation-extended.html及其 JS 逻辑覆盖默认转换与自定义规则emoji、引号样式、自动大写场景手动测试页面manual/目录提供了覆盖输入input.manual.html、删除delete.manual.html、beforeinputbeforeinput.manual.html、拼写检查、RTL、unicode、两步光标等场景的手工验证页自动化测试tests/目录包含与源码一一对应的测试如 tests/input.js、tests/delete.js、tests/texttransformation.js、tests/typing.js 等可运行pnpm run test或pnpm run test:browser验证行为。此外与 typing 配合使用的生产力特性还包括自动格式化packages/ckeditor5-autoformat、自动链接packages/ckeditor5-link、提及智能补全packages/ckeditor5-mention等它们与TextTransformation一样都建立在输入管道与TextWatcher的基础之上。小结ckeditor/ckeditor5-typing表面上是管打字的底层包实际却承载了三件关键事情以beforeinput 队列机制保证输入可靠落盘、以方向化命令与粒度控制提供符合直觉的删除行为、以可配置的转换规则带来开箱即用的 autocorrect 体验。理解Typing → Input Delete的插件结构、typing.undoStep的撤销语义以及include/remove/extra的配置组合就能在集成 CKEditor 5 时精准掌控用户的输入与编辑体验。【免费下载链接】ckeditor5Powerful rich text editor framework with a modular architecture, modern integrations, and features like collaborative editing.项目地址: https://gitcode.com/GitHub_Trending/ck/ckeditor5创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考