Vue3 + wangEditor 5.x 保姆级整合教程:从安装到图片上传(含SpringBoot后端配置)
Vue3 wangEditor 5.x 全栈整合实战从零构建富文本编辑器与文件上传系统如果你正在开发一个需要富文本编辑功能的中后台管理系统或博客平台wangEditor 5.x 结合Vue3的组合式API会是一个高效的选择。不同于市面上其他臃肿的编辑器wangEditor以轻量、易扩展著称特别适合国内开发环境。下面我将分享如何在Vue3项目中完整集成wangEditor 5.x并实现前后端分离的文件上传方案。1. 环境准备与基础集成1.1 创建Vue3项目与安装依赖首先确保你的开发环境已经配置好Node.js建议版本16和Vue CLI。使用以下命令创建一个新的Vue3项目npm init vuelatest vue3-wangeditor-demo cd vue3-wangeditor-demo npm install接下来安装wangEditor的Vue3专用包npm install wangeditor/editor wangeditor/editor-for-vue --save1.2 基础编辑器组件封装在src/components目录下创建RichTextEditor.vue文件这是我们的核心编辑器组件template div classeditor-container Toolbar :editoreditorRef :defaultConfigtoolbarConfig modedefault / Editor v-modelvalueHtml :defaultConfigeditorConfig modedefault onCreatedhandleCreated / /div /template script setup import wangeditor/editor/dist/css/style.css import { ref, shallowRef, onBeforeUnmount } from vue import { Editor, Toolbar } from wangeditor/editor-for-vue // 编辑器实例必须用shallowRef const editorRef shallowRef() const valueHtml ref(p初始内容/p) const toolbarConfig { excludeKeys: [group-video] // 排除不需要的功能 } const editorConfig { placeholder: 请输入内容..., MENU_CONF: {} } const handleCreated (editor) { editorRef.value editor } onBeforeUnmount(() { const editor editorRef.value if (editor) editor.destroy() }) /script style scoped .editor-container { border: 1px solid #ddd; border-radius: 4px; overflow: hidden; } /style关键点说明使用shallowRef而非ref存储编辑器实例避免不必要的响应式开销onBeforeUnmount生命周期中必须销毁编辑器实例防止内存泄漏通过excludeKeys可以灵活控制工具栏显示的功能项2. 深度配置与自定义功能2.1 工具栏自定义配置wangEditor允许高度自定义工具栏。以下是常用的配置示例const toolbarConfig { toolbarKeys: [ headerSelect, bold, italic, underline, color, bgColor, fontSize, fontFamily, lineHeight, |, bulletedList, numberedList, todo, |, emotion, insertLink, uploadImage, insertTable, codeBlock, divider, |, undo, redo, |, fullScreen ], excludeKeys: [group-video, insertVideo] }2.2 编辑器内容变化监听在实际应用中我们通常需要实时获取编辑器内容script setup // ...其他代码... watch(valueHtml, (newVal) { console.log(内容变化:, newVal) // 可以在这里触发自动保存等操作 }) /script3. 文件上传功能实现3.1 前端上传配置修改editorConfig中的MENU_CONF配置实现图片上传const editorConfig { placeholder: 请输入内容..., MENU_CONF: { uploadImage: { fieldName: file, server: http://your-api-domain.com/api/upload, maxFileSize: 5 * 1024 * 1024, // 5M allowedFileTypes: [image/*], timeout: 10 * 1000, // 10秒 customInsert(res, insertFn) { // 处理上传结果 if (res.errno 0) { insertFn(res.data.url, , res.data.url) } else { console.error(上传失败:, res.message) } } } } }3.2 SpringBoot后端实现创建一个简单的文件上传接口RestController RequestMapping(/api) public class FileUploadController { PostMapping(/upload) public MapString, Object uploadImage( RequestParam(file) MultipartFile file, HttpServletRequest request) { MapString, Object result new HashMap(); try { // 1. 文件校验 if (file.isEmpty()) { throw new RuntimeException(文件不能为空); } // 2. 生成唯一文件名 String originalFilename file.getOriginalFilename(); String fileExt originalFilename.substring(originalFilename.lastIndexOf(.)); String newFilename UUID.randomUUID().toString() fileExt; // 3. 确定存储路径 String uploadDir request.getServletContext().getRealPath(/uploads/); File dir new File(uploadDir); if (!dir.exists()) { dir.mkdirs(); } // 4. 保存文件 File dest new File(uploadDir newFilename); file.transferTo(dest); // 5. 构建返回结果 String fileUrl request.getScheme() :// request.getServerName() : request.getServerPort() /uploads/ newFilename; result.put(errno, 0); MapString, String data new HashMap(); data.put(url, fileUrl); data.put(alt, originalFilename); data.put(href, fileUrl); result.put(data, data); } catch (Exception e) { result.put(errno, 1); result.put(message, e.getMessage()); } return result; } }application.yml配置spring: servlet: multipart: max-file-size: 10MB max-request-size: 10MB4. 高级功能与性能优化4.1 自定义扩展菜单wangEditor支持自定义菜单项。例如添加一个插入特定模板的按钮import { DomEditor } from wangeditor/editor function insertTemplate(editor) { if (editor null) return editor.insertText(【这里是模板内容】) } const toolbarConfig { insertKeys: { index: 5, // 插入位置 keys: [insertTemplate] } } // 注册自定义菜单 Editor.registerMenu(insertTemplate, { title: 插入模板, iconSvg: svg.../svg, // 你的SVG图标 tag: button, exec(editor) { insertTemplate(editor) } })4.2 性能优化建议按需加载如果不需要所有功能可以只引入必要的模块import { Boot } from wangeditor/editor import module from wangeditor/module-name // 具体模块 Boot.registerModule(module)懒加载编辑器在需要时才加载编辑器组件template button clickshowEditor true显示编辑器/button RichTextEditor v-ifshowEditor / /template script setup import { ref } from vue const showEditor ref(false) /script内容缓存使用防抖函数定期保存编辑器内容import { debounce } from lodash-es const autoSave debounce((content) { localStorage.setItem(editor-content, content) }, 2000) watch(valueHtml, (newVal) { autoSave(newVal) })5. 常见问题解决方案5.1 编辑器初始化问题问题编辑器无法正常显示或报错解决方案确保正确引入了CSS文件检查编辑器实例是否使用了shallowRef确认组件销毁时调用了editor.destroy()5.2 图片上传失败处理问题图片上传接口返回格式不符合预期解决方案确保后端返回的JSON格式包含errno和data.url字段可以通过customInsert自定义处理逻辑customInsert(res, insertFn) { // 兼容不同后端返回格式 const url res.data?.url || res.url if (res.code 200 || res.errno 0) { insertFn(url, , url) } else { alert(res.message || 上传失败) } }5.3 内容样式问题问题编辑器中的内容在前端展示时样式不一致解决方案引入wangEditor的内容样式import wangeditor/editor/dist/css/style.css; /* 内容展示区域 */ .content-container { /* 重置一些样式 */ img { max-width: 100%; } table { border-collapse: collapse; } /* 其他样式... */ }或者使用编辑器提供的toHtml方法import { DomEditor } from wangeditor/editor const html DomEditor.toHtml(editorRef.value)在实际项目中我发现最常遇到的问题往往与编辑器实例的生命周期管理有关。特别是在使用Vue Router进行页面跳转时一定要确保在组件卸载前销毁编辑器实例否则可能导致内存泄漏。另外对于内容较多的场景建议实现自动保存功能避免用户意外丢失编辑内容。