资讯中心

Vue3 集成 wangEditor:包选择、shallowRef 与销毁

📅 2026/10/1 3:34:46
Vue3 集成 wangEditor:包选择、shallowRef 与销毁
1. 版本关系没理清后面全是白费功夫先说一个我踩过的坑Vue3 里用 wangEditor90% 的人第一次都会装错包然后对着白屏或者Cannot read properties of undefined发呆半小时。这个编辑器本身不复杂但它的包名和 Vue 版本绑定关系比较绕独立出来讲一节完全值得。1.1 Vue3 到底该装哪个包wangEditor 从 5.x 开始把「内核」和「框架适配层」拆成了两个包这个设计其实挺合理内核负责编辑能力适配层只负责把内核塞进 Vue 的组件生命周期里。所以在 Vue3 项目里你必须同时装两个npm install wangeditor/editor --save npm install wangeditor/editor-for-vuenext --save关键点在那个next。wangeditor/editor-for-vue这个包的latest标签指向的是 Vue2 版本Vue3 版本挂在next标签上。你要是直接npm i wangeditor/editor-for-vue装下来的就是 Vue2 的适配层它在 Vue3 里会尝试用this.$slots、Vue.component这类 Vue2 的东西结果就是组件渲染不出来控制台报一些你看不懂的错。我个人的做法是在package.json里把版本钉死别用波浪号{ wangeditor/editor: 5.1.23, wangeditor/editor-for-vue: 5.1.12 }为什么要钉死因为 wangEditor 官方在 2023 年之后基本停止了功能迭代仓库进入了维护状态。虽然停更了但 5.1.x 这一版功能已经非常完整实际项目里用个三五年问题不大。风险在于依赖树里如果混进了不同小版本的内核工具栏和编辑器实例对不上会出现「按钮点了没反应」这种莫名其妙的状况。锁版本是成本最低的保险。另外 Vite 项目里一般不需要额外配置但如果你用了比较激进的依赖预构建插件遇到optimizeDeps相关的报错可以把它加进排除列表// vite.config.js export default defineConfig({ optimizeDeps: { exclude: [wangeditor/editor], }, })这个不是必须的先不加真出问题了再动。1.2 为什么很多人装完就白屏白屏最常见的原因不是包的问题是样式没引入。wangEditor 的工具栏和编辑区样式都在一个独立的 CSS 文件里你不引DOM 是渲染出来了但高度、定位全是默认值看起来就像「什么都没有」。在组件里必须写这一行import wangeditor/editor/dist/css/style.css放在哪里我一般放在使用编辑器的那个组件文件顶部或者统一放main.js里。放 main.js 的好处是全局只有一份打包后不会重复放组件里的好处是按需加载只在富文本路由才加载这块 CSS。后台管理系统一般只有一个富文本页面我倾向于放组件里能省一点首屏时间。第二个白屏原因是容器没有高度。wangEditor 的编辑区域默认撑开高度但如果你把它塞进一个display: flex的父容器里或者父级有overflow: hidden它可能被压成 0 高度。这时候给个显式高度就能解决。第三个原因就比较隐蔽了如果你在script setup里用ref去接编辑器实例你会在某些交互后特别是一边打字一边触发响应式更新的场景遇到编辑器内部的诡异行为甚至直接崩掉。这个坑值得单独开一节讲。2. 从零搭一个能跑的最小闭环很多人喜欢一上来就封装组件、搞 props 设计、做主题定制结果基础跑不通排查问题的时候变量太多。我的建议是先用二十行代码跑通一个裸编辑器确认环境没问题再往上加东西。2.1 最小可用代码一个能打字、能选加粗、能插图片链接的编辑器核心就这么多template div styleborder: 1px solid #dcdfe6 Toolbar :editoreditorRef :defaultConfigtoolbarConfig modedefault styleborder-bottom: 1px solid #dcdfe6 / Editor v-modelvalueHtml :defaultConfigeditorConfig modedefault styleheight: 400px; overflow-y: hidden onCreatedhandleCreated / /div /template script setup import { onBeforeUnmount, shallowRef } from vue import wangeditor/editor/dist/css/style.css import { Editor, Toolbar } from wangeditor/editor-for-vue const editorRef shallowRef() const valueHtml shallowRef(p请输入内容.../p) const toolbarConfig {} const editorConfig { placeholder: 请输入内容... } const handleCreated (editor) { editorRef.value editor } onBeforeUnmount(() { editorRef.value?.destroy() }) /script跑起来之后你应该能看到一个带工具栏的编辑器。如果看到的是空白回上去检查 CSS 引入和容器高度。这里有几个细节值得展开mode属性有两个值default是完整模式simple是简洁模式。简洁模式只保留最基础的几个按钮适合评论框这种场景。注意Toolbar和Editor的mode要保持一致不一致会出现工具栏按钮和编辑器实例不匹配的情况。styleheight: 400px; overflow-y: hidden这个写法是官方示例里的Editor组件本身不负责滚动滚动条交给内核处理。如果你发现内容多了以后滚动条跑到外面去了基本都是这行没写对。valueHtml用了shallowRef而不是ref这个不是可选项是必须的。2.2 shallowRef 这个坑绕不开Vue3 的ref会把对象深度转换成响应式代理用Proxy包裹每一层。wangEditor 的实例对象内部持有大量 DOM 引用、事件监听器、以及一些循环引用结构。当它被 Proxy 包起来之后第一性能会掉。编辑器每次输入都会触发响应式系统的依赖收集和触发而实例上挂了几百个属性这套开销完全是浪费的因为编辑器的状态本来就不需要 Vue 去观测。第二某些 API 会失效甚至报错。因为 wangEditor 内部有些方法会做this.xxx的引用比较或者判断某个对象是不是自己创建的那个实例Proxy 之后判断会失败。我遇到过一次比较典型的现象调用editor.getText()返回的是空字符串但页面上明明有内容换成shallowRef就正常了。第三也是最危险的内存泄漏。ref持有的是代理对象销毁时如果代理没被正确释放编辑器实例就不会被 GC 回收。在后台管理系统里频繁进出富文本页面内存会一路涨上去。shallowRef只对.value这一层做响应式里面的对象原样保留正好匹配我们的需求。同理reactive和ref都不能用。顺带说一句valueHtml我也用了shallowRef。内容字符串本身是个原始值用ref也没问题但统一用shallowRef能避免以后有人改成对象类型时踩坑。2.3 销毁这一步绝不能省onBeforeUnmount里调用editor.destroy()这一行是强制的不是「建议」。wangEditor 在初始化时会往document上挂一些全局监听比如全屏状态下的keydown、resize以及拖拽上传相关的监听。不销毁的话这些监听会一直留在document上。表现出来就是你从富文本页面切走再切回来编辑器的某些行为变得奇怪比如按 Esc 会触发两个编辑器的全屏退出逻辑或者滚动时两个实例同时响应。在弹窗里用富文本时这个问题会被放大。el-dialog默认是懒渲染但内容保留你关掉弹窗再打开如果没销毁就会有多个编辑器实例同时存在第二次打开时可能出现内容串台、光标位置错乱、甚至报Cannot read properties of null这类错。我的写法是加可选链onBeforeUnmount(() { const editor editorRef.value if (editor null) return editor.destroy() editorRef.value null })顺手把editorRef.value置空能帮到 GC。如果你用的是keep-alive缓存了编辑器所在的页面那onBeforeUnmount就不会触发这时候要注意处理onActivated和onDeactivated或者在路由切换时手动销毁。不过说实话把编辑器页面放进 keep-alive 本身就不是个好主意容易出各种状态问题能不加就不加。

看完文章,想为自己的企业也做一次专业网站诊断?

尧图顾问免费为您评估现有网站,并给出建站/改版建议与报价方案。

免费获取方案