ck-editor5的研究 (4):初步使用 CKEditor5 的插件功能

article/2025/8/4 21:20:09

前言

在上一篇文章中—— ck-editor5 的研究(3):初步使用 CKEditor5 的事件系统和API ,我们已经初步了解了 CKEditor5 的工作方式

那么这篇文章,我们将初步使用 CKEditor5 的插件功能,我将会写一个自己的插件,让编辑器聚焦,并自动插入一段文本。大概的效果如下:
在这里插入图片描述

编写插件只需 3 步

1. 第一步:先搭建目录

仍然是先搭建目录结构:

  1. 新建一个 ckeditor4.ts ,使用基本的配置
  2. 新建一个 ck-editor4.client.vue 接入插件功能
  3. 新建一个 demo4/index.vue 测试编辑器
  4. 新建一个 my-plugin.ts ,用来编写我们自己的插件

他们的代码如下:

// ckeditor4.ts
import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
import { Essentials } from '@ckeditor/ckeditor5-essentials';
import { Autoformat } from '@ckeditor/ckeditor5-autoformat';
import { Bold, Italic } from '@ckeditor/ckeditor5-basic-styles';
import { BlockQuote } from '@ckeditor/ckeditor5-block-quote';
import { Heading } from '@ckeditor/ckeditor5-heading';
import { Link } from '@ckeditor/ckeditor5-link';
import { List } from '@ckeditor/ckeditor5-list';
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';export default class MyClassicEditor extends ClassicEditor {static override builtinPlugins = [Paragraph,Essentials,Autoformat,Bold,Italic,BlockQuote,Heading,Link,List,];static override defaultConfig = {toolbar: {items: ['heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo'],},language: 'en',};
}
<template><div ref="editorRef"></div>
</template><script setup lang="ts">
import MyClassicEditor from './ckeditor4';defineOptions({ name: 'CkEditor4' });type EmitsType = {ready: [editor: MyClassicEditor];destroy: [];
};
const emit = defineEmits<EmitsType>();const editorRef = useTemplateRef('editorRef');
let instance: MyClassicEditor | null = null;async function initEditor() {try {if (!editorRef.value) {return;}instance = await MyClassicEditor.create(editorRef.value);// console.log('instance :>> ', instance);emit('ready', instance); // 暴露编辑器实例,给父组件使用} catch (error: any) {console.log('error :>> ', error.stack);}
}onMounted(() => {nextTick(initEditor);
});
onBeforeUnmount(() => {if (instance) {instance.destroy();instance = null;}emit('destroy');
});
</script>

my-plugin.ts目前还是空的,而 demo4/index.vue 测试页面如下:
在这里插入图片描述
表现到页面上就是这样:在这里插入图片描述
到此为止,目录结构搭建完成,接下来准备开发插件

2. 研究文档,并编写插件

先找到官方文档 扩展功能, 进行观察,我们发现,编写插件有2种方式,第一种是使用构造函数,第二种是编写一个类名:在这里插入图片描述
于是我们进入到 my-plugin.ts 写2个插件,分别通过 构造函数 和 类名 的形式编写并进行导出:

值得注意的是,使用类名的方法,会有一个固定的 init 方法,它会被编辑器自动调用

import { Plugin } from '@ckeditor/ckeditor5-core';
import type { Editor } from '@ckeditor/ckeditor5-core';function MyPlugin1(editor: Editor) {console.log('MyPlugin1 初始化了');console.log('编辑器的实例对象是', editor);
}class MyPlugin2 extends Plugin {constructor(editor: Editor) {super(editor);console.log('MyPlugin2 constructor 执行了');}init() {console.log('MyPlugin2 init');console.log('编辑器的实例对象是', this.editor);}
}export { MyPlugin1, MyPlugin2 };

紧接着,在 ckeditor4.ts 中导入,并注册插件:

在这里插入图片描述
接着刷新页面,看到我们自己写的插件的确是被执行了,控制台有打印内容:
在这里插入图片描述

3. 在插件中添加自己的逻辑

我的现在想做一件事:初始化 2 秒之后,让编辑器聚焦,再过 2 秒后,插入一段文本,并把光标聚焦到编辑器末尾

于是对 MyPlugin2 进行改写:

class MyPlugin2 extends Plugin {constructor(editor: Editor) {super(editor);console.log('MyPlugin2 constructor 执行了');}init() {console.log('MyPlugin2 init');console.log('编辑器的实例对象是', this.editor);// 2 秒后聚焦编辑器setTimeout(() => {this.editor.focus();// 再过 2 秒后插入文本setTimeout(() => {this.editor.model.change((writer) => {const text = writer.createText('我将会在第 4 秒后被插入到编辑器中, 并且会光标被移动到编辑器的末尾');this.editor.model.insertContent(text);// 获取根节点, 并设置光标位置在末尾const root = this.editor.model.document.getRoot();if (!root) {return;}const position = writer.createPositionAt(root, 'end');writer.setSelection(position);});}, 2000);}, 2000);}
}

测试最终效果

打开 demo4/index.vue 页面,我们可以看到自己写的插件起作用了:
在这里插入图片描述

最后总结

回顾一下目前学的内容:

  1. 我们已经成功把 CKEditor5 集成到了 nuxt 中,并且封装成了一个通用的 vue 组件,可以实际在项目中使用了。
  2. 我们认识了 CKEditor5 的事件系统、常用API、插件编写。知道了 CKEditor5 的大致设计概念,比如 Model 和 2种View (即: Editing view + Data view),已经可以通过 js 对编辑器进行一定的操作了。
  3. 我们用过了好几个 Model 的方法:比如 editor.model.change((writer) => {}) 和 editor.model.document.on(‘change:data’, () => {})
  4. 我们也用过了 Editing view 的方法:比如 editor.editing.view.document.on(‘enter’, () => {})
  5. 我们也用过了 Data view 的方法:比如 editor.data.processor.toView(content) 和 editor.data.toModel(viewFragment);

而这一篇内容,我们又初步明白了 CKEditor5 的插件功能。插件有 2 中插入创建方式,一种是通过构造函数创建,另一种是定义一个 Class 类。我们更倾向于 Class 类名的形式创建。

也算是完成了一个小小的里程碑了!

看来 CKEditor5 的面纱正在被我一步步揭开,不容易啊!

之前本来打算在掘金更新的,写完之后复制过来,却意外发现 CSDN 的同学们比较热情,更加务实,专注于解决问题,并且不会吝啬自己的点赞。这也让我这种写博客的新手有动力更新。端午假期连续在家写了 4 篇内容。

相反,掘金很多文章都是凑字数、标题党,搞得太冗余繁杂了。而且大佬偏多,我发现大佬很少给别人点赞,除非那篇文章真的写得好。

后续我会继续研究 CKEditor5,并保持一颗学徒的心态,尽可能把这些经验总结出来。


http://www.hkcw.cn/article/whmkngWWwf.shtml

相关文章

分类预测 | Matlab实现CNN-LSTM-Attention高光谱数据分类

分类预测 | Matlab实现CNN-LSTM-Attention高光谱数据分类 目录 分类预测 | Matlab实现CNN-LSTM-Attention高光谱数据分类分类效果功能概述程序设计参考资料 分类效果 功能概述 代码功能 该MATLAB代码实现了一个结合CNN、LSTM和注意力机制的高光谱数据分类模型&#xff0c;核心…

嵌入式项目之mini2440系统制作烧写

系统移植的必要性与组成 在嵌入式开发中&#xff0c;**系统移植&#xff08;Linux 系统定制&#xff09;** 是常见的需求&#xff0c;主要原因在于&#xff1a; 1. **官方镜像体积过大**&#xff1a;标准 Linux 发行版&#xff08;如 Ubuntu&#xff09;可能占用数 GB 存储…

OpenShift AI - 启用过时版本的 Notebook 镜像

《OpenShift / RHEL / DevSecOps 汇总目录》 说明&#xff1a;本文已经在 OpenShift 4.18 OpenShift AI 2.19 的环境中验证 文章目录 查看可用 Notebook 镜像控制台查看命令行查看 Notebook 镜像和 Image Stream 对应关系启用老版本的 Notebook 镜像参考 查看可用 Notebook 镜…

“人单酬“理念:财税行业的自我驱动革命

引言&#xff1a;当薪酬不再是"固定数字"&#xff0c;而是"成长标尺" "为什么有人拼命工作却收入停滞&#xff1f;为什么企业总在人才流失中挣扎&#xff1f;"这些问题背后&#xff0c;往往隐藏着传统薪酬体系的僵化。而"人单酬"&…

yolo目标检测助手:具有模型预测、图像标注功能

在人工智能浪潮席卷各行各业的今天&#xff0c;计算机视觉模型&#xff08;如 YOLO&#xff09;已成为目标检测领域的标杆。然而&#xff0c;模型的强大能力需要直观的界面和便捷的工具才能充分发挥其演示、验证与迭代优化的价值。为此&#xff0c;我开发了一款基于 WPF 的桌面…

Spring Ai 从Demo到搭建套壳项目(一)初识与实现与deepseek对话模式

前言 为什么说Java长青&#xff0c;主要是因为其生态圈完善&#xff0c;Spring又做了一款脚手架&#xff0c;把对接各个LLM厂商的sdk做了一遍&#xff0c;形成一系列的spring-ai-starter-** 的依赖。 目前为止版本去到1.0.0.M6&#xff0c;golang跟不上了吧&#xff0c; Make …

机器学习实验七--SVM垃圾邮件分类器

SVM垃圾邮件分类器 一、什么是SVM二、实例&#xff1a;垃圾邮件分类器1.实验要求2.原理解释2.1 数据预处理流程2.2 特征提取方法2.3 SVM分类器 3.代码实现4.实验结果5.实验总结 一、什么是SVM 支持向量机(Support Vector Machine, SVM)是一种监督学习算法&#xff0c;主要用于…

lidar和imu的标定(一)Robust Real-time LiDAR-inertial Initialization

一、Robust Real-time LiDAR-inertial Initialization 看了这篇文章。在方法中&#xff0c;A和B都不细看了。主要看后边的几个部分。 C. LiDAR-inertial Initialization 在这一部分中&#xff0c; 1) Data Preprocess:主要是准备数据。 1.雷达里程计之后&#xff0c;可以得…

【手写系列】手写线程池

PS&#xff1a;本文的线程池为演示 Demo&#xff0c;皆在理解线程池的工作原理&#xff0c;并没有解决线程安全问题。 最简单一版的线程池 public class MyThreadPool {// 存放线程&#xff0c;复用已创建的线程List<Thread> threadList new ArrayList<>();publ…

Git企业级项目管理实战

目录 1. 准备工作 2. 添加成员 2.1 添加企业成员 2.2 添加项目成员 2.3 添加仓库开发人员 3. 开发场景 - 基于git flow模型的实践 3.1 新需求加入 3.2 修复测试环境 Bug 3.3 修改预发布环境Bug 3.4 修改正式环境 Bug 3.5 紧急修复正式环境 Bug 4. 拓展阅读 4.1 其…

go环境配置

下载对应版本的 go 版本 https://go.dev/dl/ 配置 vim ~/.zshrc export GOROOT/usr/local/go export PATH$PATH:$GOROOT/binsource ~/.zshrc >>>>>> go versiongoland 配置&#xff1a; &#x1f50d; 一、什么是GOPATH&#xff1f; GOPATH 是旧的项目结…

MySql(十二)

目录 MySql约束 1.添加主键约束 语法格式 1&#xff09;创建一个带主键的表 查看表结构 2&#xff09;创建表的时候指定主键名称 查看表结构 3&#xff09;创建一个表然后&#xff0c;然后再使用alter为列添加主键 查看表结构 4&#xff09;为表添加数据 1---正常数据 2---主键…

chrome.runtime.sendMessage 和 new FormData()

chrome.runtime.sendMessage 是Chrome扩展程序API中的一个方法&#xff0c;可用于背景脚本和内容脚本之间的消息传递。 new FormData() 提供了一种方便的方式来构建表单数据集。 在Chrome插件中&#xff0c;在 background.js 和 content.js 进行通信时使用了使用new FormData()…

数据结构-排序-排序的七种算法(2)

一&#xff0c;七种算法的介绍和比较 二&#xff0c;冒泡排序 原理&#xff1a;重复遍历列表&#xff0c;比较相邻元素&#xff0c;如果顺序错误就交换它们 时间复杂度&#xff1a; 最好&#xff1a;O(n)&#xff08;已有序时&#xff09; 平均&#xff1a;O(n) 最坏&#x…

【目标检测】backbone究竟有何关键作用?

backbone的核心在于能为检测提供若干种感受野大小和中心步长的组合&#xff0c;以满足对不同尺度和类别的目标检测。

JAVA实战开源项目:精简博客系统 (Vue+SpringBoot) 附源码

本文项目编号 T 215 &#xff0c;文末自助获取源码 \color{red}{T215&#xff0c;文末自助获取源码} T215&#xff0c;文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

IO流1——体系介绍和字节输出流

什么是io流 io流分类 纯文本文件&#xff1a; windows自带的记事本打开能读懂的 经验证&#xff1a; word&#xff0c;excel不是&#xff0c; txt, md的是纯文本文件 &#xff01;&#xff01;&#xff01;&#xff01; 字节输出流 io流体系 抽象类不能直接创建他们的对象…

告别复杂操作!电脑极简风格计时使用

无论是工作、学习还是日常生活&#xff0c;这款小巧实用的计时工具都能成为你掌控时间的好帮手。特别适合需要频繁切换正计时、倒计时和查看当前时间的场景。界面简洁&#xff0c;操作便捷&#xff0c;助你高效管理每一刻。 这是一款免安装的工具&#xff0c;下载后可直接打开…

湖北理元理律师事务所:个人债务管理的温度与精度

湖北理元理律师事务所&#xff1a;个人债务管理的温度与精度 面对信用卡、网贷、医疗债等多重债务压力&#xff0c;普通人常陷入“拆东墙补西墙”的恶性循环。湖北理元理律师事务所通过计划集团公司服务平台&#xff0c;推出“有温度的债务优化计划”&#xff0c;其人性化设计…

启动你的RocketMQ之旅(七)-Store存储原理

前言&#xff1a; &#x1f44f;作者简介&#xff1a;我是笑霸final。 &#x1f4dd;个人主页&#xff1a; 笑霸final的主页2 &#x1f4d5;系列专栏&#xff1a;java专栏 &#x1f4e7;如果文章知识点有错误的地方&#xff0c;请指正&#xff01;和大家一起学习&#xff0c;一…