Vue3与wangeditor 5.x深度集成实战:从零构建企业级富文本编辑器

在当今内容驱动的互联网产品中,富文本编辑器已成为管理后台、CMS系统等场景的标配组件。作为Vue技术栈的深度实践者,我发现wangeditor 5.x以其轻量级、高扩展性和符合中文编辑习惯的特点,正成为越来越多开发团队的首选。本文将分享如何基于Vue3的组合式API,从零构建一个支持自定义图片上传、深度集成的企业级富文本编辑解决方案。

1. 环境准备与基础集成

1.1 安装与基础配置

首先通过npm或yarn安装必要依赖:

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

不同于Vue2时代的全局注册方式,Vue3的组合式API让我们可以更灵活地管理编辑器实例。核心要点是使用 shallowRef 来避免编辑器实例的深度响应式转换:

import { shallowRef } from 'vue'
import '@wangeditor/editor/dist/css/style.css'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'

const editorRef = shallowRef()  // 关键!避免深度响应式转换
const valueHtml = ref('<p>初始内容</p>')

1.2 组件生命周期管理

wangeditor的实例管理需要特别注意Vue组件的生命周期:

onMounted(() => {
  // 可在此处模拟异步内容加载
})

onBeforeUnmount(() => {
  const editor = editorRef.value
  editor?.destroy()  // 组件卸载时销毁实例
})

注意:忘记销毁编辑器实例是常见的内存泄漏来源,特别是在SPA应用中

2. 核心配置与自定义扩展

2.1 编辑器基础配置

通过 editorConfig 对象可以定制编辑器的各种行为:

const editorConfig = {
  placeholder: '请输入内容...',
  autoFocus: false,
  scroll: true,
  hoverbarKeys: {
    link: { menuKeys: ['editLink', 'unLink'] },
    image: { menuKeys: ['editImage', 'deleteImage'] }
  }
}

2.2 自定义菜单配置

wangeditor 5.x的菜单系统采用模块化设计,可以通过 MENU_CONF 进行深度定制:

editorConfig.MENU_CONF = {
  uploadImage: {
    server: '/api/editor/upload',
    fieldName: 'file',
    maxFileSize: 10 * 1024 * 1024, // 10M
    allowedFileTypes: ['image/*'],
    timeout: 30 * 1000 // 30秒
  }
}

3. 实现自定义图片上传

3.1 服务端接口对接

现代Web应用通常需要将图片上传到自有服务器或云存储。以下是一个完整的图片上传实现方案:

editorConfig.MENU_CONF['uploadImage'] = {
  async customUpload(file, insertFn) {
    // 显示上传进度
    const progressFn = (percent) => {
      console.log(`上传进度: ${percent}%`)
    }
    
    try {
      const formData = new FormData()
      formData.append('file', file)
      
      const res = await axios.post('/api/upload', formData, {
        onUploadProgress: (e) => {
          progressFn(Math.round((e.loaded / e.total) * 100))
        }
      })
      
      // 插入到编辑器
      insertFn(res.data.url, res.data.alt || '', res.data.href || '')
    } catch (err) {
      console.error('上传失败:', err)
      // 可在此处添加自定义错误处理
    }
  }
}

3.2 上传优化实践

在实际项目中,我们还需要考虑以下优化点:

  • 图片压缩 :使用canvas在前端进行适当压缩
  • CDN加速 :返回的URL应指向CDN地址
  • 失败重试 :实现自动重试机制
  • 上传凭证 :处理云服务的临时凭证

4. 高级功能与性能优化

4.1 自定义插件开发

wangeditor的插件系统允许我们扩展编辑器功能。以下是一个简单插件的实现示例:

function withMention(editor) {
  const { insertText } = editor
  editor.insertText = (text) => {
    if (text.startsWith('@')) {
      // 处理@提及逻辑
      console.log('触发提及功能', text)
      return
    }
    insertText(text)
  }
  return editor
}

// 在创建回调中使用插件
const handleCreated = (editor) => {
  editorRef.value = withMention(editor)
}

4.2 性能优化策略

随着编辑器内容增多,性能问题可能显现。以下是几个有效的优化手段:

  • 懒加载图片 :监听编辑器滚动事件实现图片懒加载
  • 节流处理 :对频繁的change事件进行节流
  • 选择性渲染 :只在可视区域渲染复杂内容
  • 虚拟滚动 :对超长文档实现虚拟滚动
// 节流change事件示例
const handleChange = _.throttle((editor) => {
  console.log('内容变更:', editor.getHtml())
}, 500)

5. 实际项目中的经验分享

在电商后台系统的开发中,我们遇到了需要同时维护多个编辑器实例的场景。这时需要特别注意:

const editors = ref([])

const createEditor = (container) => {
  const editor = new Editor({
    selector: container,
    config: { /* 配置 */ }
  })
  editors.value.push(editor)
  return editor
}

onBeforeUnmount(() => {
  editors.value.forEach(editor => editor.destroy())
})

另一个常见需求是保存编辑器状态。我们开发了一个状态管理方案:

const saveState = () => {
  const state = {
    html: editorRef.value.getHtml(),
    selection: editorRef.value.getSelection()
  }
  localStorage.setItem('editor_state', JSON.stringify(state))
}

const restoreState = () => {
  const state = JSON.parse(localStorage.getItem('editor_state'))
  if (state) {
    editorRef.value.setHtml(state.html)
    editorRef.value.restoreSelection(state.selection)
  }
}

对于需要处理大量图片的项目,我们实现了图片的自动转存功能:当从Word粘贴内容时,自动提取其中的图片并上传到服务器。

更多推荐