wangEditor
快速开始 | wangEditor
很多后台系统(例如文章管理、公告编辑)都需要一个富文本编辑器。wangEditor 是一个轻量、好用的编辑器,非常适合在 Vue 项目里使用。
本文会从 零开始 带你实现一个可复用的 RichEditor.vue
,并讲清楚 v-model 的原理。
一、安装依赖
在 Vue3 项目里(例如 Vite + Vue3)安装最新的 wangEditor:
# 如果装过旧版本(wangeditor),先卸载
npm uninstall wangeditor# 安装 v5
npm install @wangeditor/editor @wangeditor/editor-for-vue@next
二、封装组件 RichEditor.vue
新建 src/components/RichEditor.vue
:
<template><div class="editor-wrap"><!-- 工具栏 --><Toolbar :editor="editorRef" :mode="mode" class="toolbar" /><!-- 编辑区 --><Editorv-model="valueHtml":defaultConfig="editorConfig":mode="mode"@onCreated="handleCreated"class="editor"/></div>
</template><script setup>
import { ref, shallowRef, watch, onBeforeUnmount } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import '@wangeditor/editor/dist/css/style.css'// 父组件通过 v-model 传进来的值
const props = defineProps({modelValue: { type: String, default: '' },mode: { type: String, default: 'default' }
})
const emit = defineEmits(['update:modelValue', 'change'])const editorRef = shallowRef(null) // 编辑器实例
const valueHtml = ref(props.modelValue) // 编辑器内容
const editorConfig = { placeholder: '请输入内容...' }function handleCreated(editor) {editorRef.value = editor
}// 内容变化时,通知父组件
watch(valueHtml, (val) => {emit('update:modelValue', val) // 支持 v-modelemit('change', val) // 自定义事件(可选)
})// 父组件传入的值变化时,同步到编辑器
watch(() => props.modelValue, (val) => {if (val !== valueHtml.value) valueHtml.value = val
})onBeforeUnmount(() => {const editor = editorRef.valueif (editor) editor.destroy()
})
</script><style>
.editor-wrap { border: 1px solid #ddd; border-radius: 6px; }
.toolbar { border-bottom: 1px solid #eee; }
.editor { min-height: 300px; padding: 10px; }
</style>
三、在 App.vue 中测试使用
<template><div id="app" style="padding:24px"><h2>wangEditor 测试</h2><!-- 通过 v-model 绑定内容 --><RichEditor v-model="content" @change="onChange" /><div style="margin-top:16px;"><h3>当前内容(HTML 打印)</h3><pre>{{ content }}</pre><h3>当前内容(预览效果)</h3><div v-html="content" style="border:1px dashed #ccc;padding:10px;"></div></div></div>
</template><script setup>
import { ref } from 'vue'
import RichEditor from './components/RichEditor.vue'const content = ref('<p>hello wangEditor</p>')// 监听 change 事件
function onChange(val) {console.log('内容变化:', val)
}
</script>
四、关键点解析
v-model 的原理
当你写:
<RichEditor v-model="content" />
Vue 会自动转换成:
<RichEditor :modelValue="content" @update:modelValue="val => content = val" />
也就是说:
-
:modelValue="content"
👉 父组件把值传给子组件 -
@update:modelValue="..."
👉 子组件内容变了,通知父组件更新
所以子组件必须写:
defineProps({ modelValue: String })
defineEmits(['update:modelValue'])
Vue2 的 v-model
在 Vue2 里,v-model
默认约定:
-
绑定的 prop 名:
value
-
触发的 事件名:
input
比如父组件:
<MyInput v-model="msg" />
会被编译成:
<MyInput :value="msg" @input="msg = $event" />
所以,子组件必须写:
props: {value: String
},
methods: {update(val) {this.$emit('input', val) // 触发 input 事件,父组件才能更新}
}
总结
-
Vue2:
v-model
=:value
+@input
-
Vue3:
v-model
=:modelValue
+@update:modelValue
为什么还要 emit('change', val)?
update:modelValue
是专门给 v-model 用的。
而 change
是我们自定义的事件。父组件可以用:
<RichEditor v-model="content" @change="onChange" />
这样可以在内容变化时做额外的处理(比如实时保存草稿)。
如何修改菜单配置(上传图片,视频)
菜单配置 | wangEditor
-
上传图片 →
editorConfig.MENU_CONF.uploadImage
菜单配置 | 上传图片-wangEditor -
上传视频 →
editorConfig.MENU_CONF.uploadVideo
菜单配置 |上传视频- wangEditor
默认配置
如果你啥也不配,编辑器的“上传图片/视频”按钮是不能用的,因为它没有上传地址。
如何改配置
你在创建编辑器时传入 editorConfig
,里面写上 MENU_CONF
,就能修改默认菜单的行为。
在 Vue3 (setup) 示例:
const editorConfig = {MENU_CONF: {uploadImage: {server: '/api/upload/image',fieldName: 'file',maxFileSize: 2 * 1024 * 1024, // 2M},uploadVideo: {server: '/api/upload/video',fieldName: 'file',maxFileSize: 50 * 1024 * 1024, // 50M}}
}
返回格式
-
官方推荐后端返回:
{ "errno": 0, "data": { "url": "http://xxx/yyy.png" } }
如果后端返回格式不一样,就用 customInsert
自己解析,并把 url
插入到编辑器里。
示例配置(结合若依)
const editorConfig = {MENU_CONF: {uploadImage: {server: '/api/common/upload', // RuoYi 通用上传接口fieldName: 'file',headers: { Authorization: 'Bearer ' + localStorage.getItem('token') },customInsert(res, insertFn) {// RuoYi 返回格式 { code:200, url:"/profile/upload/xxx.png" }if (res.code === 200 && res.url) {insertFn(res.url) // 插入图片,可以实现回显}}}}
}
-
res
是 wangEditor 自动传入的后端返回结果 -
insertFn
是 wangEditor 内置的插入函数 -
你只需要写逻辑,从
res
里取出正确的 URL,再insertFn(url)
总结上传图片和上传视频的配置示例
const editorConfig = {MENU_CONF: {// 图片上传uploadImage: {server: '/api/upload/image',fieldName: 'file',allowedFileTypes: ['image/*'], // 只允许图片maxFileSize: 5 * 1024 * 1024, // 5McustomInsert(res, insertFn) {if (res.code === 200 && res.url) {insertFn(res.url) // 插入 <img src="...">}}},// 视频上传uploadVideo: {server: '/api/upload/video',fieldName: 'file',allowedFileTypes: ['video/*'], // 只允许视频maxFileSize: 50 * 1024 * 1024, // 50McustomInsert(res, insertFn) {if (res.code === 200 && res.url) {insertFn(res.url) // 插入 <video src="..." controls>}}}}
}