Vue3 零基础每日笔记(081):富文本编辑器集成 wangEditor 5——封装

一、前言

公告、文章、商品详情都需要富文本(加粗/图片/列表)。本篇集成国内最常用的 wangEditor 5:Vue3 官方适配层 + 图片上传配置 + 封装成组件。先说明:wangEditor 5 官方已停止维护但依然稳定可用;若求新可评估 Tiptap/Quill,本篇以企业存量最常见的 wangEditor 为准。

bash 复制代码
npm i @wangeditor/editor @wangeditor/editor-for-vue@next
# ★ Vue3 必须装 @next 标签版本(默认 latest 是 Vue2 组件层)

二、核心组件结构

wangEditor 的 Vue 封装分两块:Toolbar(工具栏)+ Editor(编辑区),各占一行布局:

vue 复制代码
<!-- src/components/RichEditor.vue -->
<template>
  <div class="rich-editor">
    <Toolbar class="toolbar" :editor="editorRef" :defaultConfig="toolbarConfig" mode="default" />
    <Editor
      class="editor"
      v-model="valueHtml"
      :defaultConfig="editorConfig"
      mode="default"
      style="height: 300px"
      @onCreated="handleCreated"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, shallowRef, onBeforeUnmount } from 'vue'
import { Toolbar, Editor } from '@wangeditor/editor-for-vue'
import '@wangeditor/editor/dist/css/style.css'    // ★ 样式必须引入

// v-model:编辑内容(HTML 字符串)
const html = defineModel<string>({ default: '' })
const valueHtml = html                            // 语义别名,模板更直观

// ★ 编辑器实例用 shallowRef:实例内部结构复杂,不需要深度响应式(079 篇同款原则)
const editorRef = shallowRef()

const toolbarConfig = { excludeKeys: ['group-video'] }   // 排除视频组(按需)
const editorConfig = { placeholder: '请输入内容...' }

const handleCreated = (editor: any) => {
  editorRef.value = editor
}

// ★★★ 组件销毁前必须销毁编辑器实例,否则内存泄漏 + 切换路由报错
onBeforeUnmount(() => {
  editorRef.value?.destroy()
})
</script>

<style scoped>
.rich-editor { border: 1px solid var(--el-border-color); z-index: 1; }
.toolbar { border-bottom: 1px solid var(--el-border-color); }
</style>

三个关键点:

  1. CSS 必须引入 (@wangeditor/editor/dist/css/style.css),否则工具栏布局全乱
  2. 实例用 shallowRef(080 篇 ECharts 同款:重实例不做深度响应式)
  3. onBeforeUnmount 必调 destroy()------本篇头号大坑

三、使用:像普通表单一样绑 v-model

vue 复制代码
<!-- src/views/ArticleEditView.vue -->
<template>
  <el-input v-model="title" placeholder="文章标题" style="margin-bottom: 12px" />
  <RichEditor v-model="content" />
  <el-button type="primary" style="margin-top: 12px" @click="onSubmit">保存</el-button>

  <!-- 预览:富文本是 HTML,展示用 v-html -->
  <div class="preview" v-html="content"></div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import RichEditor from '@/components/RichEditor.vue'

const title = ref('')
const content = ref('<p>初始内容...</p>')

async function onSubmit() {
  // 真实项目:await createArticle({ title: title.value, html: content.value })
  console.log('提交 HTML:', content.value)
}
</script>

【截图位置:工具栏 + 编辑区输入"加粗文字和图片";下方预览区同步渲染】

四、图片上传配置

默认图片转 base64 塞进 HTML(文章一长提交体积爆炸),配置成上传服务器:

ts 复制代码
const editorConfig = {
  placeholder: '请输入内容...',
  MENU_CONF: {
    uploadImage: {
      // ★ 上传接口(063 篇的 /api 前缀方案)
      server: '/api/upload/image',
      fieldName: 'file',                     // 后端接收的文件字段名
      maxFileSize: 2 * 1024 * 1024,          // 2MB 限制
      // 后端返回格式约定:{ code: 0, data: { url: 'xxx' } } 时做映射
      customInsert(res: any, insertFn: (url: string, alt?: string) => void) {
        insertFn(res.data.url)               // ★ 把图片 url 插进编辑器
      }
    }
  }
}

后端约定:上传成功返回图片 URL,customInsert 负责把 URL 写进正文------对接任何后端格式都能适配。

五、踩坑记录

  1. 忘 destroy() :路由切换后控制台报错、内存泄漏------onBeforeUnmount 里 editorRef.value?.destroy(),本篇头号坑
  2. Vue3 装了 Vue2 的组件层 :@wangeditor/editor-for-vue 默认版本是 Vue2 组件------必须 @next
  3. 样式错乱/工具栏堆叠 :CSS 没引入------import '@wangeditor/editor/dist/css/style.css'
  4. 弹窗里 z-index 冲突 :工具栏下拉被 el-dialog 盖住------给编辑器容器加 z-index: 1(高于弹窗遮罩的层叠上下文),或弹窗 append-to-body
  5. 编辑器实例包 ref/reactive :深度代理内部复杂对象导致卡顿报错------shallowRef(080 篇同款)
  6. v-html 直接渲染用户内容:XSS 注入风险(085 篇 Checklist 有安全项)------展示端由后端过滤 XSS,或前端 DOMPurify 处理后再 v-html
  7. 回显内容丢失格式:后端存的 HTML 被转义过------确认存取都按原始 HTML 字符串,别擅自 encodeURIComponent

六、今日小结

  • 组成:Toolbar + Editor 两件套 + 必引 CSS ;Vue3 装 @next 版组件层
  • 数据流:v-model 绑 HTML 字符串,展示用 v-html(注意 XSS 过滤)
  • 实例管理:shallowRef 存实例 + onBeforeUnmount destroy()(重实例三连的第三连:080 dispose、081 destroy)
  • 图片上传:MENU_CONF.uploadImage + customInsert 适配任意后端

下篇预告

上传不止图片:头像、附件、大视频。下一篇 082 文件上传实战:el-upload 校验、自定义上传接 062 篇的 api,以及大文件分片上传的思路。

相关推荐
志尊宝1 小时前
Vue3 零基础每日笔记(080):Vue3 + ECharts 数据可视化——封装可复用图表组件
前端·javascript·vue.js·笔记·信息可视化·echarts
阿狗童鞋1 小时前
前端工程化实战指南
前端
CappuccinoRose1 小时前
CSS 动效基础
前端·css·动画·animation·transition
用户4402818278071 小时前
我用 TRAE 一晚上从零撸了个微信投票小程序: 17 个云函数 + 11 个页面的全链路实战
前端
BullSmall1 小时前
SonarQube 9.9.8-community 适配 Jenkins 版本说明
java·前端·jenkins
tryCbest1 小时前
第一篇:HTML5 核心知识全解 + 项目起步
前端·html·html5
沪上企服通2 小时前
从票据采集到凭证生成:代账 pipeline 的 OCR、查重与规则引擎设计笔记
笔记·ocr
百度一下吧2 小时前
CSS 伪类使用与实践:从入门到进阶
前端·css
hunterandroid2 小时前
android方案 : Multiplatform 跨平台开发实战:一套代码,多端运行
android·前端