尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Vue3项目集成CKEditor5:按需隐藏工具栏功能与组件封装实践

发布时间:2026/9/29 18:21:22

资讯中心
01
ARTICLE

Vue3项目集成CKEditor5:按需隐藏工具栏功能与组件封装实践

Vue3项目集成CKEditor5:按需隐藏工具栏功能与组件封装实践
去年开始转Vue3之后接了好几个后台管理端的项目无一例外都会遇到“富文本编辑”这个需求。第一反应要么是wangEditor要么是CKEditor5但真正上手之后你会发现CKEditor5的默认工具栏长得吓人一大堆功能按钮挤在两三行里客户上来就说“用不到这么多能不能简化一下”。这篇文章就把我在Vue3项目里集成CKEditor5以及按需求隐藏不必要功能的过程完整拆开讲一遍从npm安装、组件封装、工具栏裁剪到踩坑排错该给配置给配置该给代码给代码争取让你在半天之内跑通整条链路。先说一个背景我用的技术栈是 Vite Vue3 TypeScript编译环境和普通Vue3项目没有区别。CKEditor5本身不算轻量官方构建包的体积在300KB以上所以除非项目对首屏性能极其敏感否则直接官方构建包是最稳妥的方案。后续如果需要极限瘦身再走自定义构建。文章主要面向正在用Vue3做后台管理、需要快速集成编辑器且希望界面简洁的开发者如果你是刚接触Vue3没多久也能照着手把手的方式直接复现。1. 为什么选CKEditor5而不是其他编辑器1.1 市面主流编辑器横向对比富文本编辑器在Vue3生态里选择不少但真正适合“开箱即用”又方便深度定制的其实就那几个Quill、wangEditor、TinyMCE、CKEditor5。我分别试过之后说说实际感受。Quill 最大的优势是轻量和API设计漂亮但它在Vue3里要处理模块化、图片上传、字体样式等大多需要自己写不少扩展而且对表格、Word粘贴这些刚需功能的支持很弱遇到业务方要“粘贴文档后格式不乱”的诉求Quill 会让人想哭。wangEditor 是国内团队维护的中文文档友好很多后台管理系统都在用。它在基础使用上确实方便但扩展能力有限尤其是需要深度定制工具栏按钮、自定义插件的时候明显不如CKEditor5灵活而且遇到bug时社区资源相对少一些。TinyMCE 老牌但商业化味道比较重核心功能虽然开源部分扩展需要付费并且本体依赖自托管或云服务在前后端分离的部署环境下要额外处理稍显麻烦。CKEditor5 这几年我实测下来的核心优势有三个第一自身模块化架构非常清晰官方构建包自带一整套功能框架而且支持removePlugins按需移除第二官方对Vue3的支持相当到位有专门维护的 ckeditor/ckeditor5-vue 组件不需要自己硬撸原生初始化第三文档和示例覆盖面广踩到坑基本能在官方issue里找到答案。1.2 官方构建包与自定义构建的区别CKEditor5有一个非常规的设计它不像旧版编辑器那样所有功能都是运行时动态加载的而是把功能打包进“构建包Build”。你可以把构建包理解成一个“已经烘焙好的蛋糕”出来之后你可以在表面撒糖霜配置工具栏但改动内部配方就需要重新烘焙自定义构建。官方提供的构建包有 Classic、Inline、Balloon、Document 等几种。后台管理用得最多的是 Classic就是最常见的顶部工具栏加一个编辑区的形态。如果直接用官方构建包包内容基本固定但可以通过配置项来显示或隐藏界面上的按钮这也是“隐藏不需要的功能”最直接的一层操作。真正要从物理上减小包体积、去掉某段功能代码就必须走自定义构建也就是用官方提供的在线构建器或本地源码打包。自定义构建的灵活性最大但需要多一个构建产物管理的流程。文章会在后面专门讲这两个层次怎么选、怎么用这里先记住一个结论界面裁剪用于日常工作算是“软隐藏”自定义构建用于极致瘦身或者深度改造。2. Vue3里的安装与首次跑起来2.1 环境要求与依赖安装开始之前先确认你的Node版本。CKEditor5的官方构建包在较新的版本里对Node要求在16以上我用的是Node 18 LTSnpm版本9实测没有问题。如果你还在用Node 14的老环境建议先升级否则安装依赖时很容易报错。Vue3项目我这里默认已经创建好了不管是Vite还是vue-cli创建的都行注意如果是Vite项目需要在 vite.config.ts 里确保 css 处理正常后面会说到。先安装编辑器本体和Vue3的对应组件包npm install ckeditor/ckeditor5-build-classic ckeditor/ckeditor5-vue安装完成之后在 main.ts 里注册全局组件或者只在需要的组件里局部注册。我比较推荐局部注册因为编辑器并不需要在所有页面出现全局注册会额外增加一部分首屏解析消耗。局部注册方式写在一个普通组件里就行比如 EditorPage.vuescript setup langts import ClassicEditor from ckeditor/ckeditor5-build-classic; import { Ckeditor } from ckeditor/ckeditor5-vue; import { ref } from vue; const editor ClassicEditor; const editorData ref(p初始内容/p); /script template div classeditor-page ckeditor :editoreditor v-modeleditorData / /div /template就这么几行刷新页面一个带完整工具栏的CKEditor5就出来了可以编辑也可以正常输入内容。这里要注意一点ckeditor/ckeditor5-vue 在Vue3里虽然支持 v-model 语法但如果你在表单中使用官方更推荐用 :model-value 加 update:model-value 的写法防止props一个方向绑定引起数据同步异常我后面封装组件时会展开讲。2.2 样式导入的正确姿势首次集成很容易漏掉样式。CKEditor5的样式分两部分一部分是编辑器的核心界面样式另一部分是编辑器内容的排版样式比如p、h1、h2的间距等。核心界面样式在官方构建包里已经内置了只要 import 了 ClassicEditor 实例组件渲染时会自动带上。但内容区域的样式因为不同业务的字体、间距要求不一样官方不会自动注入需要自己处理。最简单的方案在全局样式文件里给编辑区加一层基础排版样式我通常这样写.ck-content { font-size: 14px; line-height: 1.8; } .ck-content h1 { font-size: 2em; margin: 0.8em 0; } .ck-content h2 { font-size: 1.5em; margin: 0.6em 0; }另外编辑器默认高度往往很矮输入几行字就出现滚动条实际项目中基本都要给编辑区设置一个最小高度。直接在样式里覆盖即可.ck-editor__editable { min-height: 300px; }这两个样式是我在每一个项目里都会加的属于“开箱后第一件要做的事”。3. 核心操作按需隐藏不需要的功能3.1 通过toolbar配置隐藏界面按钮安装好了页面能显示了现在开始动刀。隐藏功能最基本的方式是配置 toolbar 属性也就是告诉编辑器“你只需要显示这些按钮”。CKEditor5的官方构建包里默认工具条非常长包含撤销、重做、标题、字体加粗、斜体、下划线、删除线、字体颜色、背景色、列表、序号列表、引用、代码块、插入表格、插入图片、上传媒体、链接、代码、清除格式、源码编辑等等。如果业务只希望用户做基础的文字排版这个工具栏光占空间不说还容易让非技术用户误点出奇怪的内容。我的写法是把工具栏配置直接塞进 editorConfig比如一个最精简的后台内容编辑配置const editorConfig { toolbar: { items: [ undo, redo, |, heading, |, bold, italic, underline, |, bulletedList, numberedList, |, link, blockQuote, |, removeFormat ] } };然后在模板里传入ckeditor :editoreditor v-modeleditorData :configeditorConfig /刷新页面工具栏立刻变成只有你指定的那些按钮中间用 | 分隔。这个“|”是分组符号视觉上会把相关按钮用竖线隔开比如格式类按钮和列表类按钮分开界面更清晰。这里有几个实际经验想提醒你。第一toolbar 只是界面层隐藏不加载对应插件才能真正减少包的体积但对绝大多数后端管理项目来说只要界面简洁就够了代码仍然包含那些模块也不是大问题。第二有的按钮虽然写了但依然显示不出来多半是当前构建包里没有对应的插件你写了不存在的名字控制台会报一个警告然后忽略不会导致崩溃但说明配置有误。第三heading、fontFamily 这类带下拉的面板位置要特别注意别排在边缘不然点击时弹窗可能超出视口。3.2 用removePlugins移除底层插件如果你除了界面干净还想让某段功能代码彻底不执行可以用 removePlugins 配置。这个配置项的作用是在加载阶段直接跳过特定插件比如你认为项目里根本不需要“插入链接”不仅按钮不要连相关逻辑都不要那就可以这样写const editorConfig { removePlugins: [ Link, MediaEmbed, InsertTable ], toolbar: { items: [ undo, redo, |, heading, |, bold, italic, bulletedList, numberedList ] } };removePlugins 和 toolbar 配置是协同关系。removePlugins 决定哪些插件不加载toolbar 决定哪些按钮显示。逻辑上插件A如果被移除工具栏上的A按钮也不会出现。反过来你只在toolbar里去掉按钮插件仍然在后台存在。还遇到一种情况有的插件内部依赖其它插件你用 removePlugins 移除一个功能时它依赖的插件如果被一起移除了编辑器初始化会直接报错。比如移除 Image 插件时如果 MediaEmbed 还依赖着 Image 的一些方法就可能导致初始化失败。所以用 removePlugins 要保守尽量只移那些相对独立的插件像 Link、MediaEmbed、InsertTable、CodeBlock 这类独立功能比较安全。有个官方构建包里你没法安全移除的插件Paragraph。这是整个编辑器的地基移除之后编辑器连最基本的段落都无法创建。这个一定不要写进 removePlugins。3.3 自定义构建从编译层面彻底瘦身当项目对包体积有硬性要求或者需要移除的功能实在太多甚至还想加入官方构建包里没有的功能时就该考虑自定义构建了。自定义构建的思想是你自己作为“烘焙师”把需要的材料放进去烘一个完全符合自己需求的编辑器包。官方提供了在线构建器界面操作流程是选择基础编辑器类型、勾选需要的插件、配置工具栏按钮然后下载打包产物。这个产物其实就是一个新的npm包里面已经带了精简后的代码。如果你想走本地源码构建核心步骤大致是下载 ckeditor5 源码仓库在源码目录里选择基础搭包通过npm安装依赖然后用 webpack 配置打包入口最终产出你在项目里可以直接引用的编辑器文件。自定义构建的一个难点是产物版本必须与 ckeditor/ckeditor5-vue 匹配。简单理解编辑器本体的 API 版本如果和 Vue 组件包要求的版本不一致初始化时可能出现方法找不到的问题。我的建议是自定义构建产物的版本锁定在一个固定版本上不要在构建器里频繁升级。站在实际项目角度如果只是隐藏不需要的功能自定义构建属于“锦上添花”我一般会在两种场景下建议做一是CDN加载资源时需要控制首屏体积二是需要使用非官方插件或二次封装自己的专属组件。否则toolbar裁剪就能解决绝大部分需求成本低太多了。4. 封装一个可复用的Vue3编辑器组件4.1 组件Props设计与数据同步机制直接在每个页面里写一遍ckeditor组件当然可以但不出三个页面你就会觉得代码重复得厉害。我在实际项目里会封装一个自己的 EditorBox 组件把配置、样式、数据绑定、事件回调和常见的错误捕获都收敛到一起。先看组件的完整代码template div classeditor-box ckeditor :editoreditorInstance :model-valuemodelValue :configmergedConfig :disableddisabled update:model-valuehandleValueUpdate readyhandleReady focushandleFocus blurhandleBlur / /div /template script setup langts import { computed } from vue; import ClassicEditor from ckeditor/ckeditor5-build-classic; import { Ckeditor } from ckeditor/ckeditor5-vue; const props defineProps{ modelValue: string; disabled?: boolean; toolbar?: string[]; placeholder?: string; minHeight?: string; }(); const emit defineEmits{ (e: update:modelValue, value: string): void; (e: ready, editor: any): void; (e: focus, event: any): void; (e: blur, event: any): void; }(); const editorInstance ClassicEditor; const defaultToolbar [ undo, redo, |, heading, |, bold, italic, underline, |, bulletedList, numberedList, |, link, blockQuote, |, removeFormat ]; const mergedConfig computed(() ({ toolbar: { items: props.toolbar || defaultToolbar }, placeholder: props.placeholder || 请输入内容... })); function handleValueUpdate(value: string) { emit(update:modelValue, value); } function handleReady(editor: any) { manipulateEditorStyle(editor); emit(ready, editor); } function handleFocus(event: any) { emit(focus, event); } function handleBlur(event: any) { emit(blur, event); } function manipulateEditorStyle(editor: any) { const editable editor.ui.view.editable.element; if (editable props.minHeight) { editable.style.minHeight props.minHeight; } } /script style scoped .editor-box :deep(.ck-editor__editable) { min-height: v-bind(minHeight || 300px); } /style你可以在外部引用时传入一个精简的toolbar数组这样“隐藏功能”就变成了一个非常自然的复用能力不用在每个页面里重复一大堆config。4.2 数据双绑与表单校验的联动封装组件后数据更新就走 update:modelValue 事件推给父组件父组件用 v-model 正常绑定值。这里要特别说明为什么我用 :model-value 而不是直接写 v-model因为 CKEditor5 初始化是异步的内部在准备过程中可能会短暂地把 undefined 或空值回写到外部导致表单初始值被意外覆盖。手动监听 update 事件并显式 emit可控性更好。配合表单校验时比如在 Element Plus 的 Form 里el-form-item 的校验触发要求表单组件必须能响应双向绑定。自定义组件只要实现了 modelValue 的接收和 update:modelValue 的发射就可以直接塞进 el-form-item校验插件会把编辑器当成一个普通的表单控件来校验。有人可能担心一个问题编辑器初始化后数据变了会不会导致光标丢失或整个组件重新渲染这里要注意CKEditor5 内部有独立的编辑区域模型它的 model 和 view 是分离的外部modelValue变化时官方组件内部会做 diff 和同步并不会频繁销毁重建编辑器实例。实测中只要不是频繁的毫秒级变化输入体验基本无感。我在实践中还遇到过一种写入时机的问题如果你在一个弹窗里打开编辑器弹窗还没渲染完就立刻塞初始值编辑区可能出现空白。解决方法是把值放进 nextTick 里再赋值或者用 v-if 控制弹窗内容渲染完成后再创建编辑器。5. 常见问题与排坑实录5.1 编辑器样式失控与全局样式干扰最典型的一个坑项目里用了 reset.css 或者一些全局元素统一样式比如设置了p { margin: 0 }、h1, h2 { font-weight: inherit }这些会直接影响编辑器输出内容的观感导致写出来的内容看起来和Word里的完全不一样。解决思路是编辑器的内容样式要独立收敛不要跟全局样式搅在一起。可以把编辑区包裹在一个固定的类名之下例如 .editor-content然后所有针对编辑内容的样式都加在这个类作用域内并且在 build 的时候确保这些规则不会被全局覆盖。另一个视觉问题是工具栏样式和项目UI框架冲突在Element Plus项目里会比较常见。因为 CKEditor5 的按钮结构和组件库的样式作用域撞在一起后边框、圆角、颜色会出现各种“看起来有点怪”的细节。处理办法是通过覆盖CKEditor5自己的CSS变量来统一视觉CKEditor5从v35之后提供了大量CSS自定义属性你可以在组件外层作用域里写一遍.editor-box { --ck-color-base-border: #d9d9d9; --ck-color-focus-border: #409eff; --ck-border-radius: 4px; --ck-color-toolbar-background: #f5f7fa; }这样编辑器就能比较自然地融入Element Plus的视觉体系。5.2 工具栏溢出、换行与窄屏适配后台管理系统里侧边栏折叠后编辑器所在容器宽度可能会变窄工具栏按钮一多就直接换行布局会变乱。处理方式有两个一是尽量减少默认工具栏按钮数量本来需求就不需要那么多二是配置shouldNotGroupWhenFull属性当工具栏无法完整显示时自动把溢出的按钮收进“更多”下拉菜单。具体配置const editorConfig { toolbar: { items: [/* 工具栏项 */], shouldNotGroupWhenFull: true } };设置之后窄屏下工具栏会像富文本编辑器的常见习惯一样把多余按钮折叠进一个“»”下拉菜单至少界面不会错乱。不过实测发现shouldNotGroupWhenFull 放到移动端小屏上体验还是很勉强如果项目明确要在手机浏览器上编辑内容我会建议单独引用简洁工具栏配置。5.3 初始化报错与Vite构建注意点新手最常见的报错是Uncaught TypeError: Cannot read properties of undefined原因多半是 ckeditor/ckeditor5-vue 和构建包版本之间不一致或者 Vue3 组件没有正确注册。检查方案就是统一版本比如构建包和组件包都锁定到最新的稳定大版本。另一个高频率报错是在 Vite 构建阶段出现Module not found: Cant resolve ckeditor/ckeditor5-build-classic。这是因为有些 npm 包的 package.json 的 exports 字段和 Vite 的预构建逻辑冲突比较通用的解法是在 vite.config.ts 里给 optimizeDeps 增加 includeexport default defineConfig({ optimizeDeps: { include: [ckeditor/ckeditor5-build-classic, ckeditor/ckeditor5-vue] } });加完后记得重启 dev server。如果仍然报错可以把项目里的 lock 文件简单清理后再重新安装依赖。5.4 图片上传与base64塞满数据库官方经典构建点“插入图片”默认会把图片转成base64直接塞进内容。短时间内看不出问题但文章数量一多数据库体积会爆炸。这就是为什么很多后台编辑器即使“隐藏功能”时也偏要把图片上传功能留出来但上传本身要用自己的接口处理。常见的思路是拦截编辑器的上传事件改为请求自己的上传接口返回图片URL再插进编辑器内容区。CKEditor5 里通常用插件体系中的FileRepository的 adapter 来完成这部分代码就不在这里展开了但如果你的业务需要强烈建议不要依赖默认base64行为一定改造成自己的对象存储服务。我把这个点放进来是因为它和“隐藏功能”其实是同一类问题的两面。不是所有功能都要隐藏而是要搞清楚哪些功能需要留、怎么留才符合生产环境标准。编辑器集成不止是“能打字、能加粗”这么简单上传、校验、权限、样式统一这些都要提前规划。5.5 获取编辑器实例与销毁时机有些场景下你需要拿到编辑器实例比如获取纯文本内容、执行自定义命令、在编辑器外部点某个按钮向编辑区插入文本。上面的组件里我通过 ready 事件把实例发射给了父组件父组件存到变量后调用接口。编辑器实例的方法非常多常用的 mount 之后记几个就够editor.getData(); // 获取HTML字符串 editor.setData(p新的内容/p); // 覆盖内容 editor.execute(insertText, 插入的文本); // 执行命令 editor.isReadOnly; // 当前是否只读销毁方面官方组件在 vue 组件卸载时会自动销毁编辑器实例不需要额外调用。但如果你在某些情况下手动调用过editor.create()创建实例销毁就一定要对应执行editor.destroy()否则控制台会出现内存泄漏提示Vue3里的页面切换后还可能出现多个编辑器实例互相干扰。6. 一些只有动手做了才知道的细节文章最后分享几个我实际项目中反复验证过的心得。第一CKEditor5的配置项非常多但没必要一开始全部啃文档。先跑通基础再根据用户的反馈逐步调整反而更快。用户最在意的从来不是你有多少个功能按钮而是界面清爽不好出错、修改实时生效、保存后内容不变样。所以我做后台项目时工具栏都是往少里配而不是往多里堆。第二隐藏功能时一定要让需求方明确“是看不到就行”还是“彻底不能用”。前者派 toolbar 配置秒解决后者要配合 removePlugins。如果只做了界面隐藏用户用浏览器的开发者工具在编辑器事件里强行触发某个命令功能依然可能生效这在投标场景里有潜在的事故风险。第三编辑器初始化后外部异步修改内容务必尊重编辑器的数据同步机制。我曾经在某个表单页里用 watch 监听绑定值变化在里面做了一段“防止脏数据”的处理结果编辑器每次输入都会额外触发一次 setData直接把用户的光标打没了。最后定位到是自己在 watch 里重复赋值删掉就正常了。很多诡异的“编辑器卡死”和“光标乱跳”都是这个原因。第四版本锁死是王道。CKEditor5 的生态更新非常快有时候小版本升级也会带来配置兼容性问题。在 package.json 里不要用 ^ 前缀放任升级把精确版本号写死。项目上线后如无必要升级编辑器版本要谨慎给自己找麻烦的机会越少越好。集成一个富文本编辑器并不复杂真正让人头疼的是后续的边界情况。先把基础跑通再把工具栏“瘦身”最后根据业务定制这套路径在Vue3项目里可以稳定复用。希望这篇文章能让你少走几步弯路。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。