CKEditor 5 摸爬滚打(二)—— 自定义一个简单的加粗插件(上)


基于编辑器做二次开发,可能大部分的工作量都在于自定义插件

而 CKEditor 5 实现了一套自己的 MVC 架构,导致开发自定义插件尤为复杂

 

一、插件的基本架构

CKEditor 5 的自定义插件都需要从 Plugin 类继承,在此基础上根据实际情况开发三个模块:

1. editing: 插件的核心代码,注册插件对应的 Model,以及插件相关的命令、视图转换等;

2. ui: 常用的是 ButtonView,用来注册工具栏上的图标按钮;其他需要自定义的视图需要自行编写模板 Template

3. command: 自定义指令 Command,一般用于工具栏,用来控制工具栏按钮的状态和行为;也可以注册一般命令,只在代码中触发,而不暴露给用户;

 

这里提到了 Model 这个概念,它是 CKEditor 5 在编辑器中的数据模型

也就是说,我们在 CKEditor 5 中编辑内容的时候,并不是像常规编辑器那样直接编辑 DOM,而是编辑 Model

Model 其实就像是 Vue 或者 React 中的模板,每个插件需要创建自己的 schema(类似于组件),组装成类似这样的 Model

<$root>
  <paragraph>
    <$text>this is text content</$text>
  </paragraph>
  <paragraph>
    <plugin-image src="foo" title="bar"></plugin-image>
  </paragraph>
</$root>

然后通过转换器 conversion,在输出的时候将 Model 转换为富文本:

<p>this is text content</p>
<p>
  <div class="plugin-image">
    <img src="foo">
    <p class="title">bar</p>
  </div>
</p>

这里 plugin-image 的转换结果是瞎写的,在开发的时候需要自行定义转换规则

需要注意的是,由于 Model 和 conversion 的存在,一切直接操作 DOM 的开发手段都会失效

 

一下子接收到这些概念可能有点懵,不要慌,接下来用一个加粗插件的简单例子来深入了解

 

 

二、添加工具栏图标

在项目的 packages 目录下创建插件目录 plugin-bold,然后创建以下文件:


首先是 command.js:

// command.js
 import Command from "@ckeditor/ckeditor5-core/src/command"; export default class BoldCommand extends Command { refresh() { this.isEnabled = true; } execute() { console.log("Execute Plugin-Bold"); } }

这里的 BoldCommand 对象继承自 CKEditor5-Core 的 Command 类,这个类提供了三个静态属性:

1. editor: 编辑器实例;

2. value: 命令的值,有需要时可以手动修改;

3. isEnabled: 是否启用,命令被禁用时无法触发,一般会关联工具栏中的启用状态。

另外还有两个钩子函数 refresh() 和 execute()

refresh 会在编辑器更新的时候执行,类似于 React 中的 render 函数

execute 是该命令的执行函数,会在命令被触发时执行

目前只是简单的创建了 BoldCommand 这个子类,具体的逻辑后面再来开发 


然后编辑 editing.js

// editing.js
 import Plugin from "@ckeditor/ckeditor5-core/src/plugin"; import BoldCommand from "./command"; import { COMMAND_NAME__BOLD } from "./constant"; export default class BoldEditing extends Plugin { static get pluginName() { return "BoldEditing"; } init() { const editor = this.editor; // 注册一个 BoldCommand 命令
    editor.commands.add(COMMAND_NAME__BOLD, new BoldCommand(editor)); } }

eidting.js 继承自 Plugin 类,加载的时候会自动执行 init() 方法

完整的 editing.js 会包含很多内容,这里先只是注册一个 BoldCommand 命令,其他的逻辑后面补充

在通过 editor.commands.add() 方法注册命令的时候,第一个参数是命令名称,类型为字符串,我放在 constant.js 中单独维护

// constant.js
export const COMMAND_NAME__BOLD = 'ck-bold'; export const COMMAND_LABEL__BOLD = '加粗';

接下来就是加粗插件在工具栏上的按钮 toolbar-ui.js

// toolbar-ui.js
 import Plugin from "@ckeditor/ckeditor5-core/src/plugin"; import ButtonView from "@ckeditor/ckeditor5-ui/src/button/buttonview"; import boldIcon from "@ckeditor/ckeditor5-basic-styles/theme/icons/bold.svg"; import { COMMAND_NAME__BOLD, COMMAND_LABEL__BOLD } from "./constant"; export default class BoldToolbarUI extends Plugin { init() { this._createToolbarButton(); } _createToolbarButton() { const editor = this.editor; const command = editor.commands.get(COMMAND_NAME__BOLD); editor.ui.componentFactory.add(COMMAND_NAME__BOLD, (locale) => { const view = new ButtonView(locale); view.set({ label: COMMAND_LABEL__BOLD, tooltip: true, icon: boldIcon, // withText: true, // 在按钮上展示 label
        class: "toolbar_button_bold", }); // 将按钮的状态关联到命令对应值上
      view.bind("isOn", "isEnabled").to(command, "value", "isEnabled"); // 点击按钮时触发相应命令
      this.listenTo(view, "execute", () => editor.execute(COMMAND_NAME__BOLD)); return view; }); } }

这里主要是引入了 ButtonView,并基于此创建了一个按钮实例 view(属性的注释可以参考第一小节的思维导图)

然后通过 bind() 方法将按钮 view 的 isOn 状态关联到 command 命令的值 value,将 isEnabled 状态关联到命令的 isEnabled

最终通过 editor.ui.componentFactory.add() 方法创建了一个 UI 组件,该方法的第一个参数是组件名称

因为没必要创建多余变量,我直接用了命令名称 COMMAND_NAME__BOLD(也可以用别的名称,但也需要单独维护,因为后面还会用到)

创建 UI 组件之后,就可以在创建组件的时候,通过配置 toolbar 属性(在数组中添加刚才设置的组件名称)将对应的按钮展示到 toolbar 上

在 toolbar 上展示的按钮,可以通过按钮自身的 isOn 和 isEnabled 状态来高亮和禁用

另外,BoldToolbarUI 依然是继承自 Plugin 类 

 

 

三、在编辑器中引入插件

插件的三大模块已经搞定,接下来在 main.js 中引入

// plugin-bold/main.js
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; import ToolbarUI from './toolbar-ui'; import BoldEditing from './editing'; import { COMMAND_NAME__BOLD } from './constant'; export default class Bold extends Plugin { static get requires() { return [ BoldEditing, ToolbarUI ]; } static get pluginName() { return COMMAND_NAME__BOLD; } }

main.js 中的 Bold 也是 Plugin 的子类,这里有一个静态方法 requires,这个方法返回一个由 Plugin 组成的数组,用于加载依赖插件

到此为止这个插件已经可以用了,在编辑器 packages/my-editor/src/index.js 中注释掉除了 Essentials 和 Paragraph 以外的插件

并调整 create 函数中的 plugins 和 toolbar 配置项,删除被注释掉的插件

然后引入自己开发的 plugin-bold 插件

import Bold from '../../plugin-bold/main';

后面还会引入更多的自定义插件,所以最好是添加路径别名,可以在根目录的 webpack.config.js 追加配置项:

resolve: { alias: { "@plugin": path.resolve("/packages"), }, }

打包配置文件 packages/my-editor/webpack.config.js 同样需要修改:

resolve: { alias: { "@plugin": path.resolve( __dirname, "../"), }, },

然后就能用别名引入插件了:

import Bold from "@plugin/plugin-bold/main";

如果使用 VSCode 无法识别路径,可以在项目的根目录添加一个 jsconfig.json

{ "compilerOptions": { "baseUrl": "./", "paths": { "@plugin/*": ["packages/*"] } }, "exclude": ["node_modules"] }

回到编辑器文件 packages/my-editor/src/index.js,编辑 ClassicEditor.create() 方法的第二个参数中的 plugins 和 toolbar

plugins: [ Essentials, Paragraph, Bold ], toolbar: [ "undo", "redo", "|", Bold.pluginName ],

这里 toolbar 中添加的是 toolbar-ui.js 文件中 editor.ui.componentFactory.add() 创建的 UI 组件名

插件已经引入了,运行 yarn run dev 启动项目,可以看到这样的编辑器:

点击工具栏上的加粗按钮,控制台会打印 "Execute Plugin-Bold",这说明我们成功地迈出了自定义插件的第一步

接下来就是最为头疼的部分:model 与 conversion

to be continue...


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM