CKEditor深度实践:从配置到插件开发的完整指南

百里啸天 交互 阅读 2,794
赞 16 收藏
二维码
手机扫码查看
反馈

为什么我要折腾 CKEditor 的不同方案?

最近一个项目要给后台加个富文本编辑器,需求不算复杂:支持基础格式、图片上传、代码块,最好还能自定义按钮。老板说“别搞太重”,但产品经理又想要“类似 Notion 的体验”。我翻了翻老项目,发现过去三年里我用过三种 CKEditor 方案:CKEditor 4(经典版)、CKEditor 5 Classic Build、CKEditor 5 自定义构建(Custom Build)。每次选型都踩过坑,这次干脆写篇对比,省得下次再纠结。

CKEditor深度实践:从配置到插件开发的完整指南

谁更灵活?谁更省事?

先说结论:如果只是简单用,直接上 CKEditor 5 Classic Build;如果要深度定制,必须自己搭 Custom Build;至于 CKEditor 4,能不用就别用了

CKEditor 4 是我最早接触的版本,配置简单,文档齐全,但它的架构是基于 DOM 操作的,和现代前端框架(比如 React、Vue)集成起来特别别扭。而且官方已经停止功能更新,只修安全漏洞。我去年在一个老项目里被迫用它,结果为了兼容 Vue 3 的响应式数据,硬是写了两套状态同步逻辑,头疼。

CKEditor 5 Classic Build 就舒服多了。它开箱即用,几行代码就能跑起来,API 也清爽。我比较喜欢它的“模型-视图”分离架构,和框架集成天然友好。但问题在于——你只能用它打包好的功能。比如我想加个“插入公式”按钮?没门,除非你愿意接受它臃肿的 Math 插件(连 LaTeX 渲染库都给你塞进来了)。

所以,当需求稍微复杂一点,比如要限制用户只能用特定标签、或者按钮要按业务逻辑动态显示,我就得祭出 CKEditor 5 Custom Build。这玩意儿本质上是个 CLI 工具,让你从零组装插件。虽然前期要花时间配 webpack(或者 vite),但换来的是完全掌控权。亲测有效,上周我刚用它砍掉了 60% 的无用代码,首屏加载快了近 1 秒。

核心代码就这几行,但坑不少

先看最简单的 Classic Build 用法:

import { ClassicEditor } from '@ckeditor/ckeditor5-build-classic';

ClassicEditor
  .create(document.querySelector('#editor'), {
    toolbar: ['bold', 'italic', 'link', 'bulletedList', 'numberedList'],
    image: {
      toolbar: ['imageTextAlternative']
    }
  })
  .catch(error => {
    console.error(error);
  });

看起来很美好,对吧?但这里有个大坑:如果你在 SPA(比如 Vue 或 React)里动态创建/销毁编辑器,必须手动调用 destroy() 方法,否则内存泄漏到你怀疑人生。我第一次用 React 时,切换路由后编辑器残留的事件监听器直接让页面卡成幻灯片。后来才在 GitHub issue 里翻到这个提醒。

再看 Custom Build,初始化代码其实差不多,但构建过程才是重点。你得先用官方脚手架生成项目:

npm init ckeditor5 --custom my-custom-editor
cd my-custom-editor
npm install

然后在 src/ckeditor.js 里按需引入插件:

import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
import { Essentials } from '@ckeditor/ckeditor5-essentials';
import { Bold, Italic } from '@ckeditor/ckeditor5-basic-styles';
import { Link } from '@ckeditor/ckeditor5-link';
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
// 只引入你需要的,比如不要 Heading
// import { Heading } from '@ckeditor/ckeditor5-heading';

export default class MyCustomEditor extends ClassicEditor {}

MyCustomEditor.builtinPlugins = [
  Essentials,
  Bold,
  Italic,
  Link,
  Paragraph
];

MyCustomEditor.defaultConfig = {
  toolbar: ['bold', 'italic', 'link'],
  language: 'zh-cn'
};

最后构建:

npm run build

生成的 build/ckeditor.js 就是你专属的轻量版编辑器。这里注意我踩过好几次坑:别在 defaultConfig 里写太复杂的配置,有些插件依赖会漏掉。比如你用了 Image 插件但没显式引入 ImageToolbar,工具栏就出不来。官方文档写得有点绕,建议直接看插件源码里的 pluginName 和依赖声明。

我的选型逻辑

现在我基本按这个流程决策:

  • 需求简单、工期紧:直接上 CKEditor 5 Classic Build,最多通过 toolbar 配置删减按钮。省下的时间够我多喝两杯咖啡。
  • 需要深度定制或性能敏感:毫不犹豫 Custom Build。虽然前期多花半天配环境,但后期维护和加载速度优势巨大。特别是当你要集成第三方服务(比如从 jztheme.com/api/fetch-image 拉图)时,自定义上传适配器写起来特别顺手。
  • 维护老系统:如果还在用 CKEditor 4,趁重构机会换掉。它的 jQuery 依赖和全局污染在现代工程里就是定时炸弹。

另外提一嘴,CKEditor 5 的 Balloon Editor、Inline Editor 等变体其实底层都一样,只是 UI 不同。我试过 Balloon,适合注释类场景,但普通内容编辑还是 Classic 最稳。别被花哨的 demo 迷了眼,实际用起来发现光标定位和移动端兼容性反而不如 Classic。

踩坑提醒:这三点一定注意

1. 图片上传别信默认配置。CKEditor 5 默认把图片转成 base64 嵌在 HTML 里,大图直接撑爆数据库。我改成了上传到服务器,返回 URL:

// 在 Custom Build 的配置里
image: {
  upload: {
    adapter: (loader) => {
      return {
        upload: () => {
          return new Promise((resolve, reject) => {
            const formData = new FormData();
            formData.append('upload', loader.file);
            fetch('https://jztheme.com/api/upload', {
              method: 'POST',
              body: formData
            })
            .then(res => res.json())
            .then(data => {
              if (data.url) {
                resolve({ default: data.url });
              } else {
                reject(data.error);
              }
            })
            .catch(reject);
          });
        }
      };
    }
  }
}

2. 中文输入法下光标错位。这是 CKEditor 5 早期版本的通病,升级到 35+ 基本解决,但如果你用旧版,记得在 config 里加 typing: { transformations: { remove: [] } } 临时缓解。

3. 别在 SSR 里直接渲染。CKEditor 5 依赖浏览器 API,服务端渲染会报错。我在 Next.js 里用 dynamic import + { ssr: false } 绕过,虽然首屏没编辑器,但总比白屏强。

总结一下

CKEditor 5 Custom Build 是我现在的首选,尽管它需要多写点配置,但换来的是干净的 bundle 和自由的扩展能力。Classic Build 适合快速交付,但别指望它能优雅地满足复杂需求。至于 CKEditor 4,让它留在历史里吧。

以上是我踩坑后的总结,希望对你有帮助。有更优的实现方式欢迎评论区交流,比如你们怎么处理多语言内容的双向绑定?我还在找更优雅的方案。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论