依赖包 :
@wangeditor/editor-for-vue@5.1.12
富文本编辑器是现代 Web 应用中不可或缺的组件,尤其在 CMS、博客、后台管理系统等场景中广泛使用。本文将带你一步步在 Vue 3 项目中集成 WangEditor (国产开源富文本编辑器),并通过一个完整的 index.vue 组件示例,详细解析其配置项、事件处理、图片上传、表格支持及只读模式等高级功能。

一、为什么选择 WangEditor?
WangEditor 是一款轻量、易用、中文文档完善的国产富文本编辑器,具有以下优势:
- 原生支持 Vue/React
- 插件化架构,可按需扩展
- 支持 Markdown、表格、代码块、公式等
- 图片/视频上传配置灵活
- 社区活跃,中文支持好
本文基于 @wangeditor/editor-for-vue(v5+),适用于 Vue 3 Composition API。
二、安装依赖
npm install @wangeditor/editor-for-vue @wangeditor/editor
注意:editor-for-vue 是 Vue 封装层,底层仍依赖 @wangeditor/editor。
同时引入 CSS 样式:
import '@wangeditor/editor/dist/css/style.css'
三、完整组件代码解析(index.vue)
以下是我们在项目中封装的富文本组件,支持内容传入、高度控制、只读模式、图片上传、表格操作等。
1. 模板结构
<template>
<div style="border: 1px solid #ccc">
<!-- 工具栏 -->
<Toolbar
v-if="showToolbarFlag"
:editor="editorRef"
:defaultConfig="toolbarConfig"
:mode="mode"
style="border-bottom: 1px solid #ccc"
/>
<!-- 编辑器主体 -->
<Editor
v-model="valueHtml"
:defaultConfig="editorConfig"
:mode="mode"
@onCreated="handleCreated"
@onChange="handleChange"
:style="{ height: editorHeight, overflowY: 'hidden' }"
:readOnly="readOnlyFlag"
/>
</div>
</template>
- 使用
<Toolbar>和<Editor>两个组件。 - 通过
v-model绑定 HTML 内容(实际通过valueHtml手动同步)。 readOnly控制是否可编辑(但更推荐使用editor.disable(),见下文)。
2. 脚本逻辑(Composition API + TypeScript)
(1)引入与 Props 定义
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import '@wangeditor/editor/dist/css/style.css'
import base from "@/utils/base.js"
const props = defineProps({
content: { type: String, default: '' },
showToolbarFlag: { type: Boolean, default: true },
editorHeight: { type: String, default: '500px' },
readOnlyFlag: { type: Boolean, default: false }
})
const emit = defineEmits(['update'])
content:父组件传入的 HTML 字符串。update事件:将编辑后的内容回传给父组件,实现"双向绑定"。
(2)响应式变量
const mode = ref('default')
const editorRef = shallowRef() // 必须用 shallowRef,避免 Vue 深度追踪
const valueHtml = ref('')
⚠️ 注意:
editorRef必须使用shallowRef,因为编辑器实例包含大量非响应式属性,深度响应会导致性能问题甚至报错。
(3)编辑器配置 editorConfig
const editorConfig = {
placeholder: '请输入内容,单个文件的最大10MB...',
MENU_CONF: {
// 表格配置
insertTable: {
withBorder: true,
maxRow: 10,
maxCol: 6,
onInserted(tableNode) {
console.log('插入的表格:', tableNode)
}
},
// 表格悬停工具栏自定义
hoverbarKeys: {
table: {
menuKeys: [
'tableHeader',
'insertTableRow', 'deleteTableRow',
'insertTableCol', 'deleteTableCol',
'deleteTable'
]
}
}
}
}
表格功能说明:
withBorder: true:默认带边框。maxRow/maxCol:限制行列数,防止页面卡顿。hoverbarKeys:自定义表格悬停时显示的操作菜单。
(4)图片上传配置
editorConfig.MENU_CONF['uploadImage'] = {
fieldName: 'file', // 后端接收的字段名
server: `${base.baseUrl}${base.project}/admin/oss/upload`,
maxFileSize: 10 * 1024 * 1024, // 10MB
maxNumberOfFiles: 10,
allowedFileTypes: ['image/*'],
timeout: 10000,
meta: {
token: sessionStorage.getItem("token") // 携带认证信息
},
// 自定义插入逻辑
customInsert(res, insertFn, file) {
// 假设后端返回 { url: 'https://xxx.jpg' }
insertFn(res.url, res.url, res.url)
},
onFailed(file, res) {
console.log(`${file.name} 上传失败`, res)
}
}
✅ 关键点:
customInsert决定了如何将上传结果插入编辑器。meta可携带 token、用户 ID 等,用于后端鉴权。fieldName必须与后端接口参数名一致。
(5)生命周期与事件处理
初始化编辑器
const handleCreated = (editor) => {
editorRef.value = editor
if (props.readOnlyFlag) {
editor.disable() // 真正的只读(比 :readOnly 更可靠)
} else {
editor.enable()
}
}
📌 推荐使用
editor.disable()而非:readOnly属性,后者在某些版本中可能失效。
内容变化监听
const handleChange = () => {
valueHtml.value = editorRef.value.getHtml()
emit('update', valueHtml.value)
}
监听父组件 content 更新
watch(() => props.content, (newVal) => {
nextTick(() => {
if (editorRef.value) {
editorRef.value.setHtml(newVal)
valueHTML.value = newVal
}
})
})
💡 使用
nextTick确保 DOM 更新后再操作编辑器。
延迟初始化(防错处理)
onMounted(async () => {
await nextTick()
setTimeout(() => {
if (props.content && editorRef.value) {
try {
editorRef.value.setHtml(props.content)
} catch (error) {
// 防止非法 HTML(如未闭合标签)导致崩溃
const cleanHtml = props.content.replace(/<table[^>]*>.*?<\/table>/gis, '')
editorRef.value.setHtml(cleanHtml || '<p></p>')
}
}
}, 100)
})
✅ 加入
try-catch和表格清理,提升鲁棒性。
销毁编辑器(内存泄漏防护)
onBeforeUnmount(() => {
editorRef.value?.destroy()
})
3. 样式定制(表格美化)
<style scoped>
.w-e-table {
border-collapse: collapse;
width: 100%;
margin: 10px 0;
}
.w-e-table td, .w-e-table th {
border: 1px solid #ddd;
padding: 8px;
min-width: 50px;
height: 20px;
vertical-align: top;
}
.w-e-table th {
background-color: #f2f2f2;
text-align: left;
}
</style>
WangEditor 的表格默认样式较简陋,可通过覆盖 .w-e-table 类进行美化。
四、父组件使用示例
<template>
<RichTextEditor
:content="article.content"
@update="handleContentUpdate"
:editorHeight="'400px'"
:readOnlyFlag="isPreviewMode"
/>
</template>
<script setup>
const article = reactive({ content: '<p>初始内容</p>' })
const handleContentUpdate = (html) => {
article.content = html
}
const isPreviewMode = false
</script>
五、常见问题与解决方案
| 问题 | 解决方案 |
|---|---|
| 编辑器内容不更新 | 确保使用 editor.setHtml() 而非直接赋值 v-model |
| 图片上传 401 | 检查 meta 是否携带有效 token |
| 表格样式错乱 | 覆盖 .w-e-table 样式,设置 border-collapse |
| 初始化空白 | 使用 setTimeout 延迟 100ms 设置内容 |
| 内存泄漏 | 务必在 onBeforeUnmount 中调用 destroy() |
六、总结
通过本文的 index.vue 组件,我们实现了:
- ✅ 双向绑定内容
- ✅ 自定义图片上传(带 token)
- ✅ 表格增强(行列操作、样式美化)
- ✅ 只读/编辑模式切换
- ✅ 安全初始化与销毁
WangEditor 在 Vue 3 中的集成非常友好,配合 TypeScript 和 Composition API,能构建出稳定、可维护的富文本解决方案。
🔗 官方文档:https://www.wangeditor.com/
欢迎点赞、收藏、评论交流!如有疑问,可在评论区留言~