wangeditor5 在vue3项目中的正确配置

1.Editor.vue组件

html 复制代码
<script setup>
import '@wangeditor/editor/dist/css/style.css' // 引入 css
import { onBeforeUnmount, ref, shallowRef, onMounted } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'


// 定义编辑器实例,推荐用 shallowRef,避免深度监视,提升性能
const editorRef = shallowRef()

// 定义编辑器边框颜色的状态变量
const isFocused = ref(false)

// 定义mode变量
const mode = 'default'

//组件通信,传递editor内容
const emit = defineEmits(['change-content'])

// 定义编辑器HTML内容变量
const valueHtml = ref('')

// 接收父组件传递的初始内容,用于文章修改页面
const props = defineProps({
  initContent: {
    type: String,
    default: ''
  }
})

//编辑器工具栏配置
const toolbarConfig = {}

//编辑器默认配置
const editorConfig = {
  placeholder: '请输入内容...',
  autoFocus: false,
  // 初始化 MENU_CONF 对象,用于图片上传
  MENU_CONF: {
    uploadImage: {
      server: '/api/index.php/index/uploadImg',
      fieldName: 'your-custom-name',
      maxFileSize: 1 * 1024 * 1024, // 1M
      maxNumberOfFiles: 10,
      allowedFileTypes: ['image/*'],
      meta: {
        token: 'xxx',
        otherKey: 'yyy',
      },
      metaWithUrl: false,
      headers: {
        Accept: 'text/x-json',
        otherKey: 'xxx',
      },
      withCredentials: true,
      timeout: 5 * 1000, // 5 秒
      onBeforeUpload(file) {
        return file
      },
      onProgress(progress) {
        console.log('progress', progress)
      },
      onSuccess(file, res) {
        console.log(`${file.name} 上传成功`, res)
      },
      onFailed(file, res) {
        console.log(`${file.name} 上传失败`, res)
      },
      onError(file, err, res) {
        console.log(`${file.name} 上传出错`, err, res)
      },
    }
  }
}


// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
  const editor = editorRef.value
  if (editor == null) return
  editor.destroy()
})

// 在编辑器创建完成后,将父组件传来的内容同步给编辑器,
// 必须使用 editor.setHtml() 而不是直接修改 valueHtml
const handleCreated = (editor) => {
  editorRef.value = editor
  if (props.initContent) {
    editor.setHtml(props.initContent)
  }
}

//编辑器内容变化监听
const handleChange = () => {
  emit('change-content', valueHtml.value)
}


// focus功能:wangEditor提供了focus()方法,可直接调用
const focusEditor = () => {
  editorRef.value?.focus();

};

// 编辑器focus颜色改变:处理聚焦事件
const handleFocus = () => {
  isFocused.value = true
}

// 编辑器focus颜色改变:处理失焦事件
const handleBlur = () => {
  isFocused.value = false
}

// 清空编辑器内容
const clearContent = () => {
  if (editorRef.value) {
    // 根据你使用的编辑器,调用对应的清空API
    editorRef.value.clear()
  }
}


// 将聚焦方法、清空、监听内容变化暴露给父组件
defineExpose({
  focusEditor,
  clearContent,
  handleBlur
});



</script>


<template>
  <div :class="['editor-wrapper', { 'editor-focused': isFocused }]">
    <Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig" :mode="mode" />

    <!-- Editor组件中添加了一个 on-change事件来传递编辑器的值-->
    <Editor style="height: 500px; overflow-y: hidden;" v-model="valueHtml" :defaultConfig="editorConfig" :mode="mode"
      @onCreated="handleCreated" @on-change="handleChange" @onFocus="handleFocus" @onBlur="handleBlur" />

  </div>
</template>

<style scoped>
.editor-wrapper {
  border: 1px solid #ccc;
  transition: border-color 0.3s ease;
}

/* 聚焦时的蓝绿色边框 */
.editor-focused {
  border-color: #1ec4a0;
}

</style>

2.updaetArticle.vue页面

html 复制代码
<script setup>

import { useRoute, useRouter } from 'vue-router'
import { showArticle_u, updateArticle } from '@/api/updateArticle'
import { toast } from '@/components/Toast'
import { ref } from 'vue'
import Editor from '@/components/Editor.vue'
const route = useRoute()
const router = useRouter() 
const submitting = ref(false) // 防重复提交状态
const inputRef = ref(null)
const editorRef = ref(null)
const id = route.params.id
const title = ref('')
const content = ref('')

const handleChange = (val) => {
    content.value = val
}

const getArticle = async () => {
    const res = await showArticle_u({ id })
    if (res.code === 200) {
        title.value = res.data.title
        content.value = res.data.content
        document.title = "修改文章-" + res.data.title
    } else {
        console.log(res.msg)
    }
}

getArticle()

//提交文章

const handleSubmit = async () => {
    if (submitting.value) return // 【新增】如果正在提交,直接返回
    // 验证标题
    if (title.value === '') {
        toast({ msg: '标题为空', type: 'error' })
        inputRef.value?.focus()
        return
    }

    // 验证编辑器内容
    if (content.value === '<p><br></p>') {
        toast({ msg: '编辑器内容为空', type: 'error' })
        editorRef.value?.focusEditor()
        return
    }

    // 验证通过,提交
    submitting.value = true // 【新增】开启加载状态
    try {
        const res = await updateArticle({
            id: id,
            title: title.value,
            content: content.value
        })
        toast({ msg: '修改成功', type: 'success' })
        router.push(`/detail/id/${id}`)

        console.log(res)
    } catch (err) {
        console.error('请求失败', err)
        // 提示后端返回的具体错误信息
        toast({ msg: err.response?.data?.message || '提交失败,请稍后重试', type: 'error' })
        console.log(err.response?.data?.message)
    } finally {
        submitting.value = false // 【新增】无论成功失败,都关闭加载状态
    }
}

</script>


<template>
    <div class="box">
        <form @submit.prevent="handleSubmit">
            文章ID:{{ id }}
            <br /><br />
            <input ref="inputRef" class="input_title" v-model="title" placeholder="标题">
            <br /><br />

            <Editor ref="editorRef" v-if="content" :init-content="content" @change-content="handleChange" />
            <br />
            <button :disabled="submitting" class="button_form" type="submit">修改</button>
        </form>
    </div>
</template>

<style scoped>
.box {

    width: 90%;
    margin: 0 auto;
    margin-top: 50px;
}
</style>

3.注意事项

错误示例:

js 复制代码
if (props.initContent) {
  editor.setHtml(props.initContent)
}
valueHtml.value = editor.getHtml() 

在Editor.vue中,应避免同时使用editor.setHtml和editor.getHtml()。

隐患:

setHtml 是异步解析的,紧接着执行 getHtml() 时,编辑器可能还没解析完,导致拿到残缺的 HTML,然后 v-model 又把残缺的 HTML 覆盖回编辑器。

解决办法:

删掉 valueHtml.value = editor.getHtml() 这一行。v-model 会自动处理双向绑定,不需要手动赋值。

相关推荐
Cache技术分享1 小时前
517. Java 方法句柄 - 方法句柄 vs 反射 API
前端·后端
小刘在重生~1 小时前
Web 前端基础|HTML+CSS+JavaScript+Bootstrap
前端·css·html
是立不是利1 小时前
第二篇:CSS 与用户体验——看不见的设计
前端·css·ux
打呵欠的猫1 小时前
我把 20 个页面的权限控制从"硬编码"改成"配置驱动",AI 帮我生成了 80% 的迁移代码
前端·ai编程
志尊宝1 小时前
Vue3 零基础每日笔记(005):ref 响应式基础——为什么 script 里要 .value
前端·javascript·vue.js·笔记
海鸥两三1 小时前
Mac电脑使用 Tabby 部署前端项目到阿里云服务器完整教程
前端·macos·阿里云
console.log('npc')2 小时前
2026 实测:Grok 4.5 与 Grok 4.6 怎么选?前端开发、教程写作、Figma 还原选型指南
前端·大模型·ai编程·figma·grok
yyt3630458412 小时前
一次鼠标交互触发两次 Vue flushJobs:把高频交互从 VDOM 里拆出来
前端·vue.js·计算机外设
YUJIANYUE2 小时前
查立得万用查分安卓版(web环境+查询系统免安装单文件一键运行包)
android·前端