Vue3 + wangeditor 5.x 企业级封装实战:打造高可用的富文本上传组件
在后台管理系统开发中,富文本编辑器几乎是标配功能。但市面上大多数教程只停留在基础API调用层面,对于实际企业开发中遇到的样式污染、上传接口对接、组件销毁等痛点问题往往避而不谈。本文将基于Vue3的组合式API,带你从零封装一个生产环境可用的wangeditor组件,重点解决以下问题:
- 如何实现图片/视频的自定义上传逻辑
- 组件样式隔离的最佳实践方案
- 内存泄漏防范与编辑器实例销毁
- 支持双向绑定的v-model集成
- 上传进度反馈与错误处理机制
1. 环境准备与基础集成
1.1 初始化项目与安装依赖
首先确保你的项目已经配置好Vue3环境。推荐使用Vite作为构建工具:
npm create vite@latest vue3-editor-demo --template vue-ts安装wangeditor相关依赖:
npm install @wangeditor/editor @wangeditor/editor-for-vue1.2 基础组件结构
创建src/components/RichEditor.vue文件,搭建基础骨架:
<template> <div class="editor-container"> <Toolbar :editor="editorRef" :defaultConfig="toolbarConfig" mode="default" /> <Editor v-model="modelValue" :defaultConfig="editorConfig" mode="default" @onCreated="handleCreated" /> </div> </template> <script setup lang="ts"> import '@wangeditor/editor/dist/css/style.css' import { onBeforeUnmount, shallowRef } from 'vue' import { Editor, Toolbar } from '@wangeditor/editor-for-vue' const editorRef = shallowRef() const modelValue = defineModel<string>({ required: true }) const handleCreated = (editor: any) => { editorRef.value = editor } </script>关键点说明:使用
shallowRef存储编辑器实例可以避免不必要的响应式开销,这对富文本这种复杂对象尤为重要。
2. 深度配置编辑器功能
2.1 工具栏定制化
wangeditor支持高度自定义的工具栏配置。以下是企业应用中常见的配置方案:
const toolbarConfig = { excludeKeys: [ 'group-video', // 初始隐藏视频功能 'fullScreen', 'insertLink', 'codeBlock' ], insertKeys: { index: 5, keys: ['uploadImage', 'uploadVideo'] } }2.2 编辑器核心配置
针对不同场景,我们需要灵活调整编辑器的行为:
const editorConfig = { placeholder: '请输入内容...', autoFocus: false, scroll: true, maxLength: 50000, MENU_CONF: {} }3. 实现文件上传功能
3.1 图片上传方案
企业级应用通常需要对接自己的文件服务,下面是一个完整的图片上传实现:
editorConfig.MENU_CONF['uploadImage'] = { fieldName: 'file', server: '/api/upload/image', maxFileSize: 5 * 1024 * 1024, // 5MB allowedFileTypes: ['image/*'], customUpload: async (file: File, insertFn: Function) => { try { const formData = new FormData() formData.append('file', file) const { data } = await axios.post('/api/upload', formData, { headers: { 'Content-Type': 'multipart/form-data' }, onUploadProgress: (progressEvent) => { const percent = Math.round( (progressEvent.loaded * 100) / progressEvent.total ) // 可以在这里添加进度显示逻辑 } }) insertFn(data.url, '', '') } catch (error) { console.error('上传失败:', error) // 添加错误提示逻辑 } } }3.2 视频上传的特殊处理
视频上传需要额外考虑封面和预览问题:
editorConfig.MENU_CONF['uploadVideo'] = { customUpload: async (file: File, insertFn: Function) => { // 先上传视频文件 const videoRes = await uploadFile(file) // 生成视频封面缩略图 const poster = await generateVideoPoster(videoRes.url) insertFn(videoRes.url, poster) } }4. 生产环境优化策略
4.1 样式隔离方案
避免编辑器样式影响全局样式:
.editor-container { :deep(.w-e-bar) { background-color: #f8f8f8; border: 1px solid #ddd; } :deep(.w-e-text-container) { border: 1px solid #ddd !important; border-top: none !important; } }4.2 内存管理与性能优化
正确处理组件销毁:
onBeforeUnmount(() => { const editor = editorRef.value if (!editor) return editor.destroy() editorRef.value = null })4.3 扩展功能集成
实现字数统计功能示例:
const handleChange = (editor: IDomEditor) => { const text = editor.getText() const html = editor.getHtml() // 触发字数变化事件 emit('count-change', { textLength: text.length, htmlLength: html?.length || 0 }) }5. 完整组件代码与使用示例
5.1 最终组件实现
<template> <div class="rich-editor"> <Toolbar :editor="editorRef" :defaultConfig="toolbarConfig" /> <Editor v-model="modelValue" :defaultConfig="editorConfig" @onCreated="handleCreated" @onChange="handleChange" /> <div v-if="showCount" class="editor-count"> 字数: {{ count }} / {{ maxLength }} </div> </div> </template> <script setup lang="ts"> // 完整导入和配置见上文实现 </script> <style scoped lang="scss"> // 完整样式见上文实现 </style>5.2 在父组件中使用
<template> <RichEditor v-model="content" :maxLength="10000" @count-change="handleCountChange" /> </template> <script setup> const content = ref('<p>初始内容</p>') const handleCountChange = ({ textLength }) => { console.log('当前字数:', textLength) } </script>在实际项目中,这个组件已经处理了文件上传、样式隔离、内存管理等企业级关注点,可以直接集成到你的后台系统中。根据具体需求,你还可以进一步扩展如粘贴图片自动上传、自定义表情面板等功能。