以下基于ckeditor@22.0.0。
〇、CKEditor编辑器介绍
一、架构角度
二、整体分析代码
三、ckeditor5-core
四、ckeditor5-engine
五、ckeditor5-ui
六、ckeditor-utils
〇、CKEditor编辑器介绍
也可在官方提供的interactive builder网站上查看不同类型的编辑器。
1.编辑器类型
Classic editor
经典编辑器类型显示一个带有工具栏的框式编辑区域,工具栏位于页面的特定位置。”
Inline editor
内联编辑器类型允许你直接在目标位置创建内容,通过一个浮动的工具栏来帮助编辑,该工具栏在可编辑文本聚焦时出现。
Balloon editor
悬浮(balloon)编辑器类型允许你直接在目标位置创建内容,借助一个悬浮(balloon)工具栏,该工具栏在选中的可编辑文档元素旁边出现。
Balloon block editor
气球块编辑器类型允许您直接在目标位置创建内容,借助两个工具栏:
- 一个气球工具栏,出现在选中的可编辑文档元素旁边(提供内联内容格式化工具)。
- 一个块级工具栏,通过工具栏手柄按钮可访问,该按钮是附加在可编辑内容区域的拖动指示器,并随着文档中的选择而移动(提供额外的块级格式化工具)。拖动指示器按钮也是一个手柄,可用于拖放内容块。
Document editor
下面示例中的编辑器是一个功能丰富的preset,专注于类似于本地文字处理器的富文本编辑体验。它最适合用于创建通常会被打印或导出为 PDF 文件的文档。
可在DecoupledEditor 基础上创建这种类型的编辑器(及类似编辑器)并自定义 UI 布局。
Multi-root editor
多根编辑器类型是一种具有多个独立可编辑区域的编辑器类型。
使用多根编辑器与使用多个独立编辑器(如内联编辑器示例中所示)之间的主要区别在于,在多根编辑器中,所有可编辑区域都属于同一个编辑器实例,共享相同的配置、工具栏和撤销堆栈,并生成一个文档。
还有其他类型,可以参考interactive builder网站。
2.Classic editor详细介绍
2.1使用方式
方式1
// You can initialize the editor using an existing DOM element:
ClassicEditor
.create( document.querySelector( '#editor' ) )
.then( editor => {
console.log( 'Editor was initialized', editor );
} )
.catch( err => {
console.error( err.stack );
} );
方式2
// Alternatively, you can initialize the editor by passing the initial data directly as a string.
// In this case, the editor will render an element that must be inserted into the DOM:
ClassicEditor
.create( '<p>Hello world!</p>' )
.then( editor => {
console.log( 'Editor was initialized', editor );
// Initial data was provided so the editor UI element needs to be added manually to the DOM.
document.body.appendChild( editor.ui.element ); } )
.catch( err => {
console.error( err.stack );
} );
方式3
混合了方式1和方式2
// You can also mix these two ways by providing a
DOM element to be used and passing the initial data through the configuration:
ClassicEditor
.create( document.querySelector( '#editor' ),{
initialData: '<h2>Initial data</h2><p>Foo bar.</p>'
} )
.then( editor => {
console.log( 'Editor was initialized', editor ); } )
.catch( err => {
console.error( err.stack );
} );
2.2源码分析
ClassicEditor类位于ckeditor5-build-classic,继承了ckeditor5-editor-classic中的ClassicEditor类。
// https://github.com/ckeditor/ckeditor5/blob/v22.0.0/packages/ckeditor5-editor-classic/src/classiceditor.js
import Editor from '@ckeditor/ckeditor5-core/src/editor/editor';
export default class ClassicEditor extends Editor {
constructor( sourceElementOrData, config ) {
super( config );
if ( isElement( sourceElementOrData ) ) {
this.sourceElement = sourceElementOrData;
}
this.data.processor = new HtmlDataProcessor( this.data.viewDocument );
this.model.document.createRoot();
const shouldToolbarGroupWhenFull = !this.config.get( 'toolbar.shouldNotGroupWhenFull' );
const view = new ClassicEditorUIView( this.locale, this.editing.view, { shouldToolbarGroupWhenFull } );
this.ui = new ClassicEditorUI( this, view );
attachToForm( this );
}
destroy() {
if ( this.sourceElement ) {
this.updateSourceElement();
}
this.ui.destroy();
return super.destroy();
}
static create( sourceElementOrData, config = {} ) {
return new Promise( resolve => {
const editor = new this( sourceElementOrData, config );
resolve(
editor.initPlugins()
.then( () => editor.ui.init( isElement( sourceElementOrData ) ? sourceElementOrData : null ) )
.then( () => {
if ( !isElement( sourceElementOrData ) && config.initialData ) {
// Documented in core/editor/editorconfig.jdoc. // eslint-disable-next-line ckeditor5-rules/ckeditor-error-message
throw new CKEditorError( 'editor-create-initial-data', null );
}
const initialData = config.initialData || getInitialData( sourceElementOrData );
return editor.data.init( initialData );
} )
.then( () => editor.fire( 'ready' ) )
.then( () => editor )
);
} );
}
}
待后期详述。
一、架构角度(by chatgpt)
CKEditor 5 是一款功能强大的基于 JavaScript 的富文本编辑器,其设计和架构相较于前几代(如 CKEditor 4)做了重大改进。CKEditor 5 是基于现代 JavaScript 框架和模块化思想开发的,更具扩展性和可维护性。以下从架构角度详细介绍 CKEditor 5 的关键设计元素和模块:
1.模块化设计
CKEditor 5 采用模块化架构,整个编辑器被拆分为多个模块,每个模块负责一个独立的功能。这种设计使得开发者可以按需引入和扩展功能,灵活定制编辑器的行为。模块化设计基于 ES6 的模块规范,可以通过import和export来引用模块。
核心模块:CKEditor 5 的核心模块提供了基础的编辑功能,比如内容编辑、事件处理、插件管理等。
插件模块:大部分功能(如表格、图片、链接、公式等)都通过插件实现,插件的引入和扩展非常灵活。
引擎模块:CKEditor 5 中的@ckeditor/ckeditor5-engine模块是其核心的编辑引擎,负责文档模型、视图和编辑器的渲染。
2.MVC 架构
CKEditor 5 采用了类似于 MVC(Model-View-Controller)的设计模式,进一步增强了其架构的清晰性和可扩展性。
模型(Model):CKEditor 5 中的模型是数据的抽象表示,独立于任何具体的 DOM 结构。编辑器操作的数据都通过模型进行管理,保持了编辑内容的纯粹性,不会直接与 DOM 交互。这使得它对数据操作更具一致性和灵活性。
视图(View):视图是 CKEditor 5 中内容呈现的部分。视图层负责将模型的数据映射到实际的 DOM 结构中,并在用户操作时更新 DOM。视图是只读的,所有的内容修改都需要通过模型更新。
控制器(Controller):控制器在 CKEditor 5 中主要通过命令(Commands)来体现。命令负责管理用户操作和模型之间的交互,如用户输入文本、加粗、撤销等。每个命令都会修改模型,而不是直接操作视图。
3.数据模型(Data Model)
CKEditor 5 引入了强大的数据模型,这个模型是编辑内容的抽象表示,不与具体的 HTML 结构绑定。模型的数据结构类似于树状结构(类似 DOM 树),每个节点代表编辑内容中的某个元素(如段落、文本节点、加粗节点等)。这种设计带来了几个优势:
- 抽象化的内容表示:模型与最终渲染的 HTML 无关,允许 CKEditor 5 进行复杂的格式和结构管理,如多文档格式支持(例如 Markdown 或 XML)。
- 保持一致性:由于所有内容的变化首先体现在模型上,而模型变化又会反映到视图层,确保了编辑过程中的数据一致性。
4.视图模型(View Model)
视图模型(View Model)在 CKEditor 5 中负责将抽象的模型映射到具体的 HTML DOM 结构中。它通过虚拟 DOM 技术,确保高效的 DOM 操作,减少不必要的页面重绘和性能开销。
虚拟 DOM:CKEditor 5 使用虚拟 DOM 来管理视图的更新。虚拟 DOM 是对实际 DOM 的抽象,在每次更新时,它会计算出最小的修改操作来优化性能。
双向绑定:CKEditor 5 的视图与模型是双向绑定的,即视图的改变会更新模型,而模型的改变也会自动更新视图。这保证了内容在编辑器中的实时更新和同步。
5.事件驱动机制
CKEditor 5 内部大量使用了事件驱动的编程模型,使得它在扩展功能时更具灵活性。每个模块、插件以及编辑器实例都可以订阅和触发事件,从而实现组件之间的通信。
全局事件总线:CKEditor 5 提供了一个全局的事件系统,不同的模块可以通过该系统发布和监听事件。这使得各个插件或功能模块可以相互解耦,彼此独立地工作。
命令和监听器:CKEditor 5 中的命令系统基于事件来实现,编辑器中的大部分操作(如撤销、重做、格式化等)都可以通过命令触发,并且可以被监听和扩展。
6.插件系统
CKEditor 5 的插件系统是其架构中最灵活、最强大的部分之一。几乎所有功能都是通过插件来实现的,开发者可以选择性地引入或移除插件。插件之间通过依赖注入和事件机制进行通信和协作。
插件的生命周期:插件的生命周期分为初始化(init)、启动(afterInit)等多个阶段,开发者可以在这些阶段中注册逻辑来扩展或修改编辑器的行为。
插件的可扩展性:开发者可以编写自定义插件,通过继承已有插件或编写全新的功能模块,来扩展 CKEditor 5 的功能。此外,CKEditor 5 的核心功能也都是通过插件形式实现的,因此其功能非常灵活。
7.文档协作功能
CKEditor 5 提供了实时协作功能,支持多个用户同时编辑同一文档。这个功能是通过一个复杂的协作引擎来实现的,利用了操作变换(Operational Transformation, OT)和 CRDT(Conflict-free Replicated Data Types)等技术来确保多个用户同时编辑的内容冲突得到合理处理。
实时协作引擎:CKEditor 5 的协作引擎会跟踪每个用户的操作,并将它们同步到所有参与编辑的客户端。在这种架构中,编辑内容首先在模型层进行操作,然后通过网络同步给其他用户的编辑器实例,确保所有人看到的内容一致。
8.文档输出与输入
CKEditor 5 支持多种文档格式的输入与输出。由于其内容模型与 HTML 等具体格式解耦,因此可以方便地将编辑器内容转换为多种格式。
HTML 数据处理:通过@ckeditor/ckeditor5-html-support插件,CKEditor 5 可以解析和输出复杂的 HTML 结构。
Markdown 和其他格式:借助不同的插件,CKEditor 5 也可以支持将内容转换为 Markdown、JSON 等格式。
9.可定制性与配置
CKEditor 5 的可定制性非常强,几乎所有功能和行为都可以通过配置选项或扩展插件来调整。开发者可以基于现有的模块或插件,实现自定义的行为逻辑。
编辑器配置:通过传递不同的配置对象,可以控制 CKEditor 5 的默认行为,如工具栏、快捷键、内容过滤规则等。
主题与样式:CKEditor 5 提供了灵活的样式和主题机制,开发者可以通过修改 CSS 或自定义主题来更改编辑器的外观。
10.性能优化
CKEditor 5 在性能优化上做了大量工作,特别是在处理大型文档和复杂结构时。
延迟加载:通过模块化设计和按需加载,CKEditor 5 支持延迟加载部分插件或功能,减少初始加载时间。
批量操作:CKEditor 5 通过批处理机制将多个编辑操作组合在一起,减少不必要的视图更新和 DOM 操作。
CKEditor 5 的架构以模块化、MVC 和事件驱动为核心,充分考虑了现代 Web 开发中的可扩展性、可维护性和性能优化。其基于数据模型的设计使得它能够轻松支持多种文档格式,并且插件化的架构让开发者能够灵活地扩展和定制功能。这种设计使得 CKEditor 5 成为一款适合现代 Web 应用的高度可扩展的富文本编辑器。