实战:基于 DecoupledEditor 构建类 Word 的纸面编辑体验)
CKEditor 5 文档编辑器Document Editor实战基于 DecoupledEditor 构建类 Word 的纸面编辑体验【免费下载链接】ckeditor5Powerful rich text editor framework with a modular architecture, modern integrations, and features like collaborative editing.项目地址: https://gitcode.com/GitHub_Trending/ck/ckeditor5本文以 docs/examples/builds/document-editor.md 的文档编辑器Document Editor示例为主线结合 packages/ckeditor5-editor-decoupled/docs/framework/document-editor.md 框架教程与ckeditor5-editor-decoupled包的源码完整讲解如何基于DecoupledEditor打造一个接近原生文字处理软件如 Word、Google Docs的编辑体验。读完本文你将掌握文档编辑器预设的定位、解耦式 UI 的底层原理、从零搭建纸面风格界面的完整 HTML/CSS/JS 方案以及如何输出并定制自己的文档编辑器。文档编辑器预设是什么文档编辑器Document editor是 CKEditor 5 提供的一种编辑器预设preset。正如其官方示例文档所描述它提供了一种与原生文字处理软件相近的编辑体验最适合用来创作那些后续会被打印或导出为 PDF的文档如邀请函、报告、信函等。与经典Classic、内联Inline、气泡Balloon等预设不同文档编辑器预设构建在解耦式编辑器DecoupledEditor之上——工具栏与编辑区是相互独立的 DOM 元素可以由你自由决定它们在页面中的摆放位置。因此它能够呈现页面居中、工具栏浮在上方、纸面可滚动的类 Word 视觉风格。在官方示例页面中你可以看到这个预设的真实效果示例源码见 docs/_snippets/examples/document-editor.js 与 docs/_snippets/examples/document-editor.html完整的编辑器类型对比可参考 docs/examples/index.md。技术基石DecoupledEditor 的解耦式 UI 架构要理解文档编辑器必须先理解它的底座DecoupledEditor。在 packages/ckeditor5-editor-decoupled/src/decouplededitoruiview.ts 中官方对DecoupledEditorUIView的定义非常明确它是一个虚拟视图提供内联的可编辑区editable、工具栏toolbar和菜单栏menuBarView但不对这些组件在 DOM 中的排列方式做任何假定。这意味着DecoupledEditor不会像ClassicEditor那样把工具栏和编辑区自动组装成一个整体容器而是把决定布局的权力完全交给开发者。从 packages/ckeditor5-editor-decoupled/src/decouplededitorui.ts 的DecoupledEditorUI#init()可以看到编辑区 UI 与编辑引擎的根节点editingRoot共享名称用于 ARIA 可访问性标识——这为任意自定义布局下的无障碍支持如工具栏键盘导航奠定了基础。文档编辑器正是利用了这一特性工具栏与编辑区分别被注入到页面中两个独立容器配合 CSS 呈现纸面效果。快速开始初始化文档编辑器根据框架教程packages/ckeditor5-editor-decoupled/docs/framework/document-editor.md文档编辑器可以使用DOM 中已有的数据容器来初始化也可以接受一段原始数据字符串并让编辑器自行创建可编辑区。最典型的初始化方式如下通过DecoupledEditor.create()的root.element选项指定编辑区挂载点然后使用getData()方法获取输出数据。import { DecoupledEditor } from ckeditor5; DecoupledEditor.create( { root: { element: document.querySelector( .document-editor__editable ) }, cloudServices: { // CKEditor 云服务的配置用于图片上传、CKBox 等云功能。 // ... } } ) .then( editor { const toolbarContainer document.querySelector( .document-editor__toolbar ); // 关键一步把编辑器工具栏挂到页面中你指定的容器里。 toolbarContainer.appendChild( editor.ui.view.toolbar.element ); window.editor editor; } ) .catch( err { console.error( err ); } );这里有一个必须注意的时序问题工具栏元素editor.ui.view.toolbar.element只有在编辑器 UI 触发EditorUI#ready事件之后才可用事件定义见 packages/ckeditor5-ui/src/editorui/editorui.ts。create()返回的 Promise 在编辑器就绪后 resolve因此appendChild操作写在.then()回调中是安全的。页面骨架承载工具栏与编辑区的 HTML 结构上面的 JS 代码只负责运行编辑器用户界面还需要你亲自搭建。文档编辑器推荐使用如下两层容器结构一个容纳工具栏另一个容纳可编辑区纸面。div classdocument-editor div classdocument-editor__toolbar/div div classdocument-editor__editable-container div classdocument-editor__editable pThe initial editor data./p /div /div /divdiv classdocument-editor是最外层容器虽然并非强制要求但官方推荐用它把所有组件聚合在一起便于统一控制布局。编辑器启动时会自动把工具栏注入.document-editor__toolbar把编辑区可编辑元素注入.document-editor__editable。⚠️ 必须确保编辑器创建时上述 HTML 结构已经存在于 DOM 中。做法是把引导代码放在 HTML 稍靠后的位置或者使用DOMContentLoaded事件延迟 JavaScript 执行直到 DOM 就绪。在官方示例 docs/_snippets/examples/document-editor.html 中.document-editor__editable内预置了完整的示例文档数据标题、段落、图片、表格、引用块等编辑器初始化后这些内容会立即成为可编辑内容。样式化把编辑区变成一张纸纸面效果完全由 CSS 实现。框架教程给出了一套完整的样式方案分四步逐步构建。外层容器与浮动工具栏首先定义主容器设置纵向边界并使用 Flex 纵向排列让工具栏固定在上方、编辑区在下方滚动。.document-editor { border: 1px solid var(--ck-color-base-border); border-radius: var(--ck-border-radius); /* 为文档编辑器设置纵向边界。 */ max-height: 700px; /* 作为 Flex 容器便于渲染。 */ display: flex; flex-flow: column nowrap; }然后让工具栏看起来像悬浮在纸面上方.document-editor__toolbar { /* 确保工具栏容器始终位于编辑区之上。 */ z-index: 1; /* 制造工具栏悬浮在编辑区之上的错觉。 */ box-shadow: 0 0 5px hsla( 0,0%,0%,.2 ); /* 使用 CKEditor 的 CSS 变量保持 UI 一致。 */ border-bottom: 1px solid var(--ck-color-toolbar-border); } /* 调整工具栏在容器内的观感。 */ .document-editor__toolbar .ck-toolbar { border: 0; border-radius: 0; }注意上述样式大量使用--ck-*开头的 CSS 变量如--ck-color-base-border、--ck-border-radius、--ck-color-toolbar-border、--ck-spacing-large它们来自 CKEditor 5 的主题体系能保证自定义布局与编辑器 UI 在视觉上完全一致。编辑区A4 纸面效果编辑区容器要表现得像原生文字处理软件的内部——居中放置一张纸纸在容器内可上下滚动/* 让编辑区容器看起来像原生文字处理应用的内部。 */ .document-editor__editable-container { padding: calc( 2 * var(--ck-spacing-large) ); background: var(--ck-color-base-foreground); /* 允许滚动纸张页面。 */ overflow-y: scroll; } .document-editor__editable-container .ck-editor__editable { /* 设置纸张的尺寸接近 A4 比例。 */ width: 15.8cm; min-height: 21cm; /* 让纸张与容器边界保持距离。 */ padding: 1cm 2cm 2cm; border: 1px hsl( 0,0%,82.7% ) solid; border-radius: var(--ck-border-radius); background: white; /* 纸张投下轻微阴影3D 错觉。 */ box-shadow: 0 0 5px hsla( 0,0%,0%,.1 ); /* 水平居中纸张。 */ margin: 0 auto; }这里以cm为单位设置页面尺寸15.8cm宽、21cm高配合白色背景、圆角与浅阴影呈现出一张真实纸页的视觉效果。内容排版与标题样式接下来是编辑内容的排版。官方强调建议使用.ck-content类来为编辑器内容标题、段落、列表等编写视觉样式同时保证标题下拉菜单中的预览与正文实际渲染效果一致。先定义页面默认字体/* 设置页面内容的默认字体。 */ .document-editor .ck-content, .document-editor .ck-heading-dropdown .ck-list .ck-button__label { font: 16px/1.6 Helvetica Neue, Helvetica, Arial, sans-serif; }再调整标题下拉菜单的预览尺寸并同步定义 H1–H3 与段落的样式注意标题层级与 HTML 标签的对应关系H1 对应h2H2 对应h3H3 对应h4/* 调整标题下拉菜单容纳更大的标题样式。 */ .document-editor .ck-heading-dropdown .ck-list .ck-button__label { line-height: calc( 1.7 * var(--ck-line-height-base) * var(--ck-font-size-base) ); min-width: 6em; } /* 缩小下拉菜单中的标题预览保持相对比例。 */ .document-editor .ck-heading-dropdown .ck-list .ck-button:not(.ck-heading_paragraph) .ck-button__label { transform: scale(0.8); transform-origin: left; } /* Heading 1 的样式。 */ .document-editor .ck-content h2, .document-editor .ck-heading-dropdown .ck-heading_heading1 .ck-button__label { font-size: 2.18em; font-weight: normal; } .document-editor .ck-content h2 { line-height: 1.37em; padding-top: .342em; margin-bottom: .142em; } /* Heading 2 的样式。 */ .document-editor .ck-content h3, .document-editor .ck-heading-dropdown .ck-heading_heading2 .ck-button__label { font-size: 1.75em; font-weight: normal; color: hsl( 203, 100%, 50% ); } .document-editor .ck-heading-dropdown .ck-heading_heading2.ck-on .ck-button__label { color: var(--ck-color-list-button-on-text); } .document-editor .ck-content h3 { line-height: 1.86em; padding-top: .171em; margin-bottom: .357em; } /* Heading 3 的样式。 */ .document-editor .ck-content h4, .document-editor .ck-heading-dropdown .ck-heading_heading3 .ck-button__label { font-size: 1.31em; font-weight: bold; } .document-editor .ck-content h4 { line-height: 1.24em; padding-top: .286em; margin-bottom: .952em; } /* Paragraph 的样式。 */ .document-editor .ck-content p { font-size: 1em; line-height: 1.63em; padding-top: .5em; margin-bottom: 1.13em; }引用块使用衬线字体并加大左右留白使文档更显精致/* 让引用文本使用衬线字体并增加留白。 */ .document-editor .ck-content blockquote { font-family: Georgia, serif; margin-left: calc( 2 * var(--ck-spacing-large) ); margin-right: calc( 2 * var(--ck-spacing-large) ); }响应式细节官方示例docs/_snippets/examples/document-editor.html还补充了响应式适配视口宽度 ≤ 960px 时缩小纸面内边距2cm改为1.5em视口宽度 ≤ 1200px 时宽内容区域中的纸面宽度改为100%视口宽度 ≥ 1360px 时为main__content-wide布局移除右侧内边距视口宽度 ≥ 1600px 时纸面宽度按视口比例60%调整保持纸面观感。组装功能完整的文档编辑器示例官方示例预设docs/_snippets/examples/document-editor.js展示了文档编辑器在生产级场景下的完整配置可作为你自定义工具栏与插件的直接参考import { TableColumnResize } from ckeditor5; import { CS_CONFIG, TOKEN_URL, DecoupledEditor, getViewportTopOffsetConfig, setViewportTopOffsetDynamically } from snippets/index.js; DecoupledEditor .create( { root: { element: document.querySelector( .document-editor__editable ) }, extraPlugins: [ TableColumnResize ], cloudServices: CS_CONFIG, ckbox: { tokenUrl: TOKEN_URL, forceDemoLabel: true, allowExternalImagesEditing: [ /^data:/, origin, /ckbox/ ] }, toolbar: { items: [ undo, redo, |, heading, |, bold, italic, |, link, insertImage, insertTable, mediaEmbed, |, bulletedList, numberedList, outdent, indent ] }, ui: { viewportOffset: { top: getViewportTopOffsetConfig() } } } ) .then( editor { document .querySelector( .document-editor__toolbar ) ?.appendChild( editor.ui.view.toolbar.element ); window.editor editor; setViewportTopOffsetDynamically( editor ); } ) .catch( err { console.error( err ); } );这份配置透露了几个值得关注的实战要点extraPlugins: [ TableColumnResize ]通过额外插件引入TableColumnResize为示例文档中的表格提供列宽拖拽调整能力说明文档编辑器示例的插件是按需组合的而非全量打包。toolbar.items的完整列表撤销/重做、标题、粗体/斜体、链接、插入图片、插入表格、媒体嵌入、无序/有序列表、减少/增加缩进。分隔符|用于在工具栏中分组。ui.viewportOffset与getViewportTopOffsetConfig()官方文档站顶部有固定导航栏因此通过viewportOffset让编辑器的浮动 UI如下拉面板、气泡避开顶部遮挡并用setViewportTopOffsetDynamically在运行时动态更新该偏移量。在实际集成中如果你的页面也有固定头部这个配置同样适用。cloudServices与ckbox用于启用 CKEditor 云服务与 CKBox图片管理。其中allowExternalImagesEditing允许编辑来自data:URI、同源地址及ckbox域名下的外部图片——注意这些是示例演示环境snippets/index.js中导出的测试配置的用法生产环境应替换为你自己的凭证。获取与输出文档数据编辑完成后通过编辑器实例的getData()方法即可拿到当前文档的 HTML 输出用于保存到后端或进一步处理例如交给 PDF 导出服务// 假设 editor 是 DecoupledEditor.create() 返回的实例。 const htmlOutput editor.getData(); // 将 htmlOutput 提交到你的存储服务或交给打印/PDF 导出流程。因为文档编辑器预设的核心应用场景就是创作后续要打印或导出为 PDF 的文档建议在集成时让输出数据保持干净的语义化 HTML标题、段落、列表、表格结构完整以兼容常见的 PDF 渲染管线。getData()的完整定义见 packages/ckeditor5-core/src/editor/editor.ts。总结与进一步定制文档编辑器示例展示了 CKEditor 5 预设与框架两种层级的结合DecoupledEditor提供解耦的 UI 架构开发者自由布局工具栏与编辑区CSS 负责把编辑区渲染成居中的纸面工具栏配置决定功能集。如果你想在此基础上进一步定制官方框架教程packages/ckeditor5-editor-decoupled/docs/framework/document-editor.md还给出了明确的扩展方向高亮配置 packages/ckeditor5-highlight/src/highlightconfig.ts 中的HighlightConfig为文档加入荧光笔效果示例文档中的at least half an hour earlier即使用了高亮背景字体大小配置 packages/ckeditor5-font/src/fontsize.ts 的FontSizeConfig字体族配置 packages/ckeditor5-font/src/fontfamily.ts 的FontFamilyConfig让文档排版更接近正式印刷物。得益于DecoupledEditor这个可自由排版的底座你完全可以在此基础上尝试更多自定义 UI 布局——例如把菜单栏置于页面顶部、把侧边栏与工具栏组合同时保留完整的功能集与无障碍支持如工具栏键盘导航。这正是文档编辑器预设接近原生文字处理软件体验的根本来源。【免费下载链接】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),仅供参考