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