uniapp富文本编辑器实战:从图片上传到内容导出全流程解析(附完整代码)

在移动应用和Web开发中,富文本编辑功能已经成为内容型应用的标配需求。无论是博客系统、新闻发布后台还是社区论坛,都需要为用户提供强大的图文混排能力。uniapp作为跨平台开发框架,通过editor组件为开发者提供了统一的富文本编辑解决方案,本文将深入探讨其实战应用中的关键技术与完整实现流程。

1. 编辑器核心架构与初始化

uniapp的editor组件本质上是一个基于Webview的富文本编辑容器,它封装了不同平台的底层实现差异,为开发者提供一致的API接口。与传统的contenteditable方案相比,editor组件在非H5平台通过原生渲染实现,确保了跨平台的功能一致性。

典型初始化配置示例:

<editor 
  id="myEditor"
  class="editor-container"
  placeholder="开始创作..."
  show-img-size
  show-img-toolbar
  @ready="onEditorReady"
  @focus="onEditorFocus"
  @statuschange="onStatusChange"
></editor>

关键属性说明:

  • show-img-size:启用图片尺寸显示
  • show-img-toolbar:启用图片工具栏
  • @ready:编辑器初始化完成事件

获取编辑器实例需要结合uni.createSelectorQuery():

onEditorReady() {
  uni.createSelectorQuery()
    .in(this)
    .select('#myEditor')
    .fields({
      context: true
    }, (res) => {
      this.editorCtx = res.context
      console.log('编辑器实例获取成功', this.editorCtx)
    }).exec()
}

注意:编辑器实例的获取必须在组件mounted之后进行,建议在@ready事件中处理

2. 图片上传的完整实现方案

图片处理是富文本编辑中最复杂的环节之一,需要兼顾本地选取、上传过程和最终插入三个关键阶段。以下是经过实战验证的实现方案:

2.1 多平台兼容的图片选择

async chooseImages() {
  const [err, res] = await uni.chooseImage({
    count: 3,
    sizeType: ['original', 'compressed'],
    sourceType: ['album', 'camera']
  })
  
  if (err) {
    uni.showToast({ title: '选择图片失败', icon: 'none' })
    return
  }
  
  this.uploadImages(res.tempFiles)
}

2.2 分步上传与进度反馈

async uploadImages(files) {
  const uploadTasks = files.map(file => {
    return new Promise((resolve, reject) => {
      const uploadTask = uni.uploadFile({
        url: 'https://api.example.com/upload',
        filePath: file.path,
        name: 'file',
        success: (res) => {
          const data = JSON.parse(res.data)
          resolve(data.url)
        },
        fail: reject
      })
      
      uploadTask.onProgressUpdate((res) => {
        console.log(`上传进度: ${res.progress}%`)
      })
    })
  })
  
  try {
    const imageUrls = await Promise.all(uploadTasks)
    this.insertImages(imageUrls)
  } catch (e) {
    uni.showModal({ title: '上传失败', content: e.message })
  }
}

2.3 智能插入与样式控制

insertImages(urls) {
  urls.forEach(url => {
    this.editorCtx.insertImage({
      src: url,
      width: '100%',
      data: {
        customAttr: 'image-' + Date.now()
      },
      success: () => {
        console.log('图片插入成功')
      }
    })
  })
}

图片处理最佳实践:

  • 采用CDN地址而非本地路径
  • 添加自定义data属性便于后续处理
  • 统一宽度样式避免布局问题
  • 实现上传失败的重试机制

3. 内容格式控制与交互优化

专业的富文本编辑器需要提供丰富的格式控制能力,同时保持流畅的用户体验。

3.1 文本样式控制API

// 加粗/取消加粗
toggleBold() {
  this.editorCtx.format('bold', !this.isBold)
}

// 标题级别切换
setHeading(level) {
  this.editorCtx.format('header', level ? `h${level}` : false)
}

// 文字颜色设置
setTextColor(color) {
  this.editorCtx.format('color', color)
}

3.2 状态同步与UI反馈

通过@statuschange事件实现工具栏状态同步:

onStatusChange(e) {
  const { detail } = e
  this.formatState = {
    bold: detail.hasOwnProperty('bold'),
    italic: detail.hasOwnProperty('italic'),
    header: detail.header || false
    // 其他格式状态...
  }
}

3.3 自定义分割线与特殊内容

// 插入分割线
insertDivider() {
  this.editorCtx.insertDivider({
    success: () => console.log('分割线插入成功')
  })
}

// 插入自定义HTML
insertHTML(html) {
  this.editorCtx.insertHTML(html)
}

4. 内容导出与安全处理

内容导出是富文本编辑的最后关键环节,需要处理多种格式和安全性问题。

4.1 多格式导出实现

exportContent() {
  this.editorCtx.getContents({
    success: (res) => {
      console.log('HTML内容:', res.html)
      console.log('纯文本内容:', res.text)
      console.log('Delta格式:', res.delta)
      
      // 处理导出内容
      this.processContent(res.html)
    }
  })
}

4.2 XSS防护与内容过滤

import xss from 'xss' // 使用xss库

processContent(html) {
  const options = {
    whiteList: {
      a: ['href', 'title', 'target'],
      img: ['src', 'alt', 'width'],
      p: [],
      span: ['style']
      // 其他允许的标签和属性...
    },
    css: {
      whiteList: {
        color: true,
        'background-color': true
      }
    }
  }
  
  this.cleanHtml = xss(html, options)
}

4.3 与服务端的交互方案

async submitContent() {
  const { html, text } = await this.getEditorContent()
  
  const res = await uni.request({
    url: '/api/content',
    method: 'POST',
    data: {
      html,
      text,
      summary: text.substring(0, 140)
    }
  })
  
  if (res.success) {
    uni.showToast({ title: '发布成功' })
  }
}

5. 性能优化与异常处理

在实际项目中,富文本编辑器可能面临性能瓶颈和各类异常情况。

5.1 大内容加载策略

// 分块加载内容
loadLargeContent(content) {
  const chunkSize = 50000
  let offset = 0
  
  const loadNextChunk = () => {
    const chunk = content.substring(offset, offset + chunkSize)
    this.editorCtx.insertHTML(chunk, () => {
      offset += chunkSize
      if (offset < content.length) {
        setTimeout(loadNextChunk, 100)
      }
    })
  }
  
  loadNextChunk()
}

5.2 内存管理与防崩溃

// 定期清理历史记录
setInterval(() => {
  this.editorCtx.clearHistory()
}, 300000)

// 错误边界处理
window.onerror = (message) => {
  if (message.includes('editor')) {
    console.error('编辑器异常:', message)
    uni.showModal({
      title: '编辑器异常',
      content: '建议保存当前内容后刷新页面'
    })
  }
}

5.3 平台差异处理方案

// 条件编译处理平台差异
// #ifdef MP-WEIXIN
this.editorConfig.maxImageSize = 1048576 // 微信小程序限制
// #endif

// #ifdef H5
this.editorConfig.maxImageSize = 5242880 // H5限制更大
// #endif

在真实项目开发中,我们发现编辑器性能与内容体积呈指数关系变化。当内容超过2万字时,建议采用分段加载策略。对于图片密集型内容,提前压缩和CDN加速是必要的优化手段。

更多推荐