完全指南:用 `_getModelData()` / `_setModelData()` 操控模型与视图)
CKEditor 5 测试辅助工具Testing Helpers完全指南用_getModelData()/_setModelData()操控模型与视图【免费下载链接】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 框架开发工具development tools中的 Testing Helpers 展开系统讲解_getModelData()、_setModelData()以及视图侧_getViewData()、_setViewData()等开发辅助函数的字符串化与解析机制、完整选项参数与底层实现原理。读完本文你将能够在自己的编辑器插件测试中用一行字符串精确断言模型/视图的结构、选区与标记大幅提升测试的可读性与编写效率。提示本指南的官方出处为 docs/framework/development-tools/testing-helpers.md所有源码依据均来自 packages/ckeditor5-engine/src/dev-utils 及其对应的测试用例。什么是 Testing Helpers_getModelData()与_setModelData()是 CKEditor 5 引擎在 model 开发工具模块 和 view 开发工具模块 中暴露的一组开发辅助函数。它们解决了一个非常实际的痛点如何用一段可读的字符串来描述内存中的数据结构。在 CKEditor 5 中编辑器内容同时存在于两个层次模型Model编辑器的内部数据结构例如paragraph、$text节点视图View由模型经 downcast 转换得到、面向编辑界面的树结构。关于二者的架构关系可参考 编辑引擎架构文档。Testing Helpers 的核心能力是字符串化Stringify把模型/视图的结构、选区selection、范围range与位置position输出为一个 HTML 风格的字符串装载Load从字符串反解析把内容写回模型/视图同时还原选区。由于断言结果是一段纯文本测试的期望值与实际值可以直接对比这在编写插件测试、调试转换流程时极其常用。import { _getModelData } from ckeditor5; ClassicEditor .create( { root: { initialData: pHello bworld/b!/p } } ) .then( editor { console.log( _getModelData( editor.model ) ); // - paragraph[]Hello $text boldtrueworld/$text!/paragraph } );上面来自 原文档 的示例展示了_getModelData()的典型用法它返回一段类似 XML 的字符串其中paragraph表示模型段落元素$text boldtrue表示带属性加粗的文本节点[]则标记出当前文档选区的折叠位置。⚠️重要警告这两组工具是为原型开发、调试与测试而设计的请勿在生产级代码中使用它们。这一点在原文档以及 model.ts 源码注释 中都有明确强调。模型字符串的语法规则要熟练使用这些助手必须先理解其输出/输入字符串的语法。以_getModelData()的输出为例源码规范见 model.ts普通文本直接输出文本内容如Hello块级元素输出为 HTML 风格标签如paragraph.../paragraph带属性的文本节点输出为$text attributevalueText data/$text形式。例如boldtrue表示文本带加粗属性这是 model 工具独有的$text特殊标记元素属性直接作为标签属性输出例如paragraph alignmentcenter选区用方括号[]标记范围[]表示折叠collapsed选区。下表总结了模型字符串的核心符号符号含义示例paragraph块级元素paragraphfoo/paragraph$text boldtrue带属性的文本节点$text boldtrueworld/$text[]折叠选区paragraph[]Hello/paragraph[]展开范围paragraph[Hello]/paragraph在字符串化时属性值会经过类型还原源码中的parseAttributeValue()model.ts会尝试用JSON.parse把字符串还原为原始类型例如true变回布尔值true、1变回数字1、{x:1,y:2}变回对象解析失败则保留为字符串。_getModelData()把模型输出为字符串_getModelData( model, options? )接收一个Model实例返回当前文档的字符串化内容。其完整签名与选项定义见 model.ts 的 GetModelDataOptions选项类型默认值说明withoutSelectionbooleanfalse为true时输出结果中不包含选区信息rootNamestringmain指定要从哪个根root字符串化内容多根编辑器可传入其他根名convertMarkersbooleanfalse为true时把标记markers也包含进输出字符串convertMarkers的用法可以对照 model.js 测试一个折叠标记foo会被输出为foo:start/foo:start非折叠标记则输出foo:start/foo:start...foo:end/foo:end且标记按名称排序以保证输出结果稳定。底层实现_stringifyModel_getModelData()内部委托给_stringifyModel()model.ts。它的工作流程如下根据传入节点构造覆盖范围并把ModelSelection/ModelPosition/ModelRange统一转换为 selection创建一个临时的Model与EditingViewViewRootEditableElement作为main根组装DowncastDispatcher及一组 converterinsertText()、insertAttributesAndChildren()、insertElement()、选区转换的convertRangeSelection()/convertCollapsedSelection()、标记转换的insertUIElement()调用downcastDispatcher.convert()与convertSelection()完成模型 → 视图的转换再借助视图侧_stringifyView()把视图树输出为字符串移除临时div根标签并把内部占位元素名model-text-with-attributes替换回$text。从源码可以看出_getModelData()的输出不是简单的手写序列化而是完整走了一遍 downcast 转换管线因此它反映的是真实转换结果这也是它在调试转换逻辑时特别有价值的原因。_setModelData()从字符串装载模型_setModelData( model, data, options? )把 HTML 风格的字符串解析并写入模型文档同时重建选区。其选项定义见 model.ts 的 SetModelDataOptions选项类型默认值说明rootNamestringmain解析后的数据写入哪个根selectionAttributesRecordstring, unknown—附加到选区上的属性集合lastRangeBackwardbooleanfalse为true时最后一个范围按 backward反向选区创建batchTypeBatchType—指定插入元素使用的批次类型不传则走model.change()传入则走model.enqueueChange()inlineObjectElementsArraystring—需要按内联对象inline object处理的元素名列表典型用法import { _setModelData } from ckeditor5; _setModelData( editor.model, paragraphHello $text boldtrueworld/$text!/paragraph ); // 写入模型并把折叠选区放在段落开头 _setModelData( editor.model, paragraph[]Hello/paragraph ); // 写入一个跨段落的展开选区 _setModelData( editor.model, paragraph[Foo/paragraphparagraphBar]/paragraph ); // 创建一个 backward 选区并给选区附加属性 _setModelData( editor.model, paragraph[Foo]/paragraph, { lastRangeBackward: true, selectionAttributes: { foo: bar } } );使用前提在_setModelData()解析元素之前必须在模型的 schema 中注册这些元素否则会抛出转换错误。这一点在源码注释model.ts与转换器实现中都有体现——convertToModelElement()会通过conversionApi.schema.checkChild()校验元素是否被允许model.ts不允许的位置会抛出Element x was not allowed in given position.。底层实现_parseModel_setModelData()内部委托给_parseModel()model.ts流程如下把$text替换为合法的 XML 元素名model-text-with-attributes调用视图侧的_parseView()解析出视图树与选区组装UpcastDispatcher注册documentFragment、element:model-text-with-attributes、element、text四类 converter通过upcastDispatcher.convert()完成视图 → 模型的 upcast 转换用Mapper把视图选区映射为模型选区必要时附加selectionAttributes。测试用例model.js验证了各种边界情况纯文本、带选区的文本、元素内嵌套选区、Unicode 文本、backward 选区、rootName指定的特殊根等。其中还验证了batchType选项的行为——传入batchType时调用model.enqueueChange()不传时调用model.change()。自定义根注意_parseModel默认使用context: $root。若编辑器使用自定义根通过RootConfig.modelElement配置需要显式传入目标根元素或其模型元素名作为 context否则转换结果可能不正确。相关细节可参阅 Schema 深度解析 中的自定义根章节。视图侧工具_getViewData()与_setViewData()除了模型工具view.ts 还提供了视图层的对应助手_getViewData( view, options? )与_setViewData( view, data, options? )。它们面向EditingView实例用于检查视图树与视图选区的状态。视图字符串化有几个独特选项GetViewDataOptions选项说明showType输出元素类型前缀如container:p、attribute:b、empty:img、ui:spanshowPriority输出属性元素的优先级如b view-priority10renderUIElements输出ViewUIElement的内部 HTML 内容renderRawElements输出ViewRawElement的内部 HTML 内容domConverter传入真实ViewDomConverter可让转换走与编辑视图完全相同的过滤流程否则使用简化 stubskipListItemIds为true默认时隐藏列表项随机生成的data-list-item-id属性便于稳定断言视图选区标记[]与{}视图工具在选区标记上与模型工具略有不同这是调试视图时最需要留意的细节元素间范围用[与]标记如p[bfoobar/b]/p文本内部范围用{与}标记如pbf{ooba}r/b/p通过sameSelectionCharacters: true可统一为[]模型工具内部正是使用该选项把两者统一见 view.ts。_parseView()的解析器由RangeParser类实现view.ts它会从文本节点中提取括号标记并重建ViewRange同时支持通过order数组重排多个范围的顺序、用lastRangeBackward标记最后一个范围为反向。如果遇到未闭合的]、嵌套的[、文本节点中间的元素级[]等情况解析器会抛出明确的解析错误。元素名的类型前缀如container:p在_convertElement()中view.ts被转换回ViewContainerElement、ViewAttributeElement、ViewEmptyElement、ViewUIElement、ViewRawElement等真实视图节点类型view-priority与view-id属性则被解析为元素优先级与 id。在测试中的典型应用模式Testing Helpers 最常见的应用场景是编写插件/特性的单元测试测试覆盖可参考 packages/ckeditor5-engine/tests/dev-utils 目录下的model.js、view.js、utils.js、operationreplayer.js。一个典型的写入—断言闭环测试模式如下import { _setModelData, _getModelData } from ckeditor5; import { Model } from ckeditor5/src/engine.js; describe( 自定义插件的模型转换, () { let model, document, root; beforeEach( () { model new Model(); document model.document; root document.createRoot(); // 在 schema 中注册测试用元素 model.schema.register( paragraph, { inheritAllFrom: $block } ); // 手动填充内容 _setModelData( model, paragraphFoo!/paragraph ); } ); it( 应该正确插入文本与选区, () { _setModelData( model, []paragraphBar!/paragraph ); expect( _getModelData( model ) ).toBe( []paragraphBar!/paragraph ); } ); } );类似上面的测试结构在 model.js 测试文件 中可以看到完整范本先注册 schemaa、b、c、paragraph等元素及其允许的属性再通过_setModelData()铺设数据最后用_getModelData()断言输出完全一致。此外这两个函数的可测试性也经过了专门设计_getModelData暴露_stringify属性、_setModelData暴露_parse属性用于测试中的 spy 监控这在 model.ts 源码 和对应测试中都有体现。更多开发调试辅助utils 与 OperationReplayerdev-utils目录下还包含其他开发调试工具utils.ts提供convertMapToTags()把 Map 转为keyvalue标签格式、convertMapToStringifiedObject()转为 JSON 对象字符串、dumpTrees()/initDocumentDumping()/logDocument()按版本快照并回放文档树用于调试以及printTree()树打印。注意该文件顶部注释特别说明这些函数仅限内部调试使用依赖默认构建流程中不存在的特殊方法因此没有配套测试operationreplayer.ts提供OperationReplayer类用于按版本回放模型操作帮助定位协同编辑或复杂操作序列中的状态变化其测试见 operationreplayer.js。这两个工具与_getModelData()/_setModelData()共同组成了引擎的完整开发工具箱。使用注意事项与最佳实践结合 原文档、源码与测试总结以下几点实践建议仅在开发/测试环境使用这些函数刻意命名为_前缀并归入dev-utils生产构建中不应出现先注册 schema 再装载数据_setModelData()依赖 schema 校验未注册的元素会导致解析报错注意$text语法的唯一性带属性的文本必须写成$text attributevalue这是模型工具的特有表示法与视图/HTML 的b标签不同多根编辑器指定rootName需要操作非main根时读写两侧都要显式传rootName见 model.js 的特殊根测试稳定断言优先使用withoutSelection: true当测试只关心内容结构、不关心选区时关闭选区输出能让断言更聚焦对列表内容视图工具默认隐藏随机生成的listItemId保证了断言的确定性调试转换流程时善用convertMarkers标记markers常用于评论、高亮等特性_getModelData( model, { convertMarkers: true } )能直观看到标记在模型中的位置。小结Testing Helpers 把 CKEditor 5 复杂的模型/视图结构降维成了可读、可比较的字符串是插件开发者日常测试与调试的得力工具。本文详细拆解了模型侧与视图侧的四个核心函数、它们支持的选项参数、底层 downcast/upcast 实现链路以及配套的调试工具并给出了可直接套用的测试模式。掌握这些工具后你可以为任何自定义插件写出精确、可维护、可读性极强的单元测试。更多框架开发工具请继续阅读 development-tools 目录 下的其他文档。【免费下载链接】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),仅供参考