news 2026/4/29 5:36:32

Vue3 + wangEditor 5.x 保姆级整合教程:从安装到图片上传(含SpringBoot后端配置)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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 vue@latest vue3-wangeditor-demo cd vue3-wangeditor-demo npm install

接下来安装wangEditor的Vue3专用包:

npm install @wangeditor/editor @wangeditor/editor-for-vue --save

1.2 基础编辑器组件封装

src/components目录下创建RichTextEditor.vue文件,这是我们的核心编辑器组件:

<template> <div class="editor-container"> <Toolbar :editor="editorRef" :defaultConfig="toolbarConfig" mode="default" /> <Editor v-model="valueHtml" :defaultConfig="editorConfig" mode="default" @onCreated="handleCreated" /> </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) // 可以在这里触发自动保存等操作 }) </script>

3. 文件上传功能实现

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 Map<String, Object> uploadImage( @RequestParam("file") MultipartFile file, HttpServletRequest request) { Map<String, 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); Map<String, 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: 10MB

4. 高级功能与性能优化

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 性能优化建议

  1. 按需加载:如果不需要所有功能,可以只引入必要的模块:
import { Boot } from '@wangeditor/editor' import module from '@wangeditor/module-name' // 具体模块 Boot.registerModule(module)
  1. 懒加载编辑器:在需要时才加载编辑器组件:
<template> <button @click="showEditor = true">显示编辑器</button> <RichTextEditor v-if="showEditor" /> </template> <script setup> import { ref } from 'vue' const showEditor = ref(false) </script>
  1. 内容缓存:使用防抖函数定期保存编辑器内容:
import { debounce } from 'lodash-es' const autoSave = debounce((content) => { localStorage.setItem('editor-content', content) }, 2000) watch(valueHtml, (newVal) => { autoSave(newVal) })

5. 常见问题解决方案

5.1 编辑器初始化问题

问题:编辑器无法正常显示或报错
解决方案

  1. 确保正确引入了CSS文件
  2. 检查编辑器实例是否使用了shallowRef
  3. 确认组件销毁时调用了editor.destroy()

5.2 图片上传失败处理

问题:图片上传接口返回格式不符合预期
解决方案

  1. 确保后端返回的JSON格式包含errnodata.url字段
  2. 可以通过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 内容样式问题

问题:编辑器中的内容在前端展示时样式不一致
解决方案

  1. 引入wangEditor的内容样式:
@import '@wangeditor/editor/dist/css/style.css'; /* 内容展示区域 */ .content-container { /* 重置一些样式 */ img { max-width: 100%; } table { border-collapse: collapse; } /* 其他样式... */ }
  1. 或者使用编辑器提供的toHtml方法:
import { DomEditor } from '@wangeditor/editor' const html = DomEditor.toHtml(editorRef.value)

在实际项目中,我发现最常遇到的问题往往与编辑器实例的生命周期管理有关。特别是在使用Vue Router进行页面跳转时,一定要确保在组件卸载前销毁编辑器实例,否则可能导致内存泄漏。另外,对于内容较多的场景,建议实现自动保存功能,避免用户意外丢失编辑内容。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/29 5:36:30

代码生成模型评估与工业应用实践指南

1. 项目背景与核心价值去年在参与一个企业级代码生成工具选型时&#xff0c;我们团队花了整整三周时间对比了市面上主流的12种代码生成模型。当时最头疼的问题就是&#xff1a;不同研究机构发布的基准测试结果差异巨大&#xff0c;有的模型在HumanEval榜单上表现优异&#xff0…

作者头像 李华
网站建设 2026/4/29 5:36:22

GMM聚类与SelfStepConf结合的数学推理框架解析

1. 项目背景与核心价值数学推理作为人工智能领域的核心挑战之一&#xff0c;其关键在于如何让机器像人类一样理解数学问题的内在逻辑。传统方法往往依赖规则引擎或符号计算&#xff0c;但在处理模糊边界问题时表现欠佳。我们团队尝试将GMM&#xff08;高斯混合模型&#xff09;…

作者头像 李华
网站建设 2026/4/29 5:35:48

AI文件整理工具:本地LLM智能分类与重命名实战指南

1. 项目概述与核心价值如果你和我一样&#xff0c;常年被电脑里堆积如山的文件搞得焦头烂额——下载文件夹里塞满了IMG_2023_01_01.jpg、document_final_v2_revised.pdf、song_unknown.mp3这类不知所云的文件&#xff0c;每次找东西都像大海捞针——那么&#xff0c;AI File So…

作者头像 李华
网站建设 2026/4/29 5:34:45

5步搞定游戏操作冲突:Hitboxer SOCD清洁工具完全指南

5步搞定游戏操作冲突&#xff1a;Hitboxer SOCD清洁工具完全指南 【免费下载链接】socd Key remapper for epic gamers 项目地址: https://gitcode.com/gh_mirrors/so/socd 在激烈的游戏对战中&#xff0c;你是否曾因同时按下相反方向键导致角色卡顿、连招中断而错失胜利…

作者头像 李华
网站建设 2026/4/29 5:34:27

8B小模型干翻GPT-4o?用“信息不对称“让LLM自己查自己的幻觉

&#x1f3af; 核心摘要 RAG&#xff08;检索增强生成&#xff09;本意是让LLM"有据可查"&#xff0c;减少胡说八道。但现实很骨感&#xff1a;模型在自我验证时&#xff0c;往往会"自证清白"——因为验证器看到了原始回答&#xff0c;天然倾向于确认而非…

作者头像 李华
网站建设 2026/4/29 5:32:27

Qwen3-ASR与Docker集成:容器化部署指南

Qwen3-ASR与Docker集成&#xff1a;容器化部署指南 1. 引言 语音识别技术正在快速改变我们与设备交互的方式&#xff0c;而Qwen3-ASR作为支持52种语言和方言的开源模型&#xff0c;为开发者提供了强大的语音转文字能力。但在实际部署中&#xff0c;环境配置依赖、版本兼容性等…

作者头像 李华