在vue3中实现富文本--利用defineCustomElement来快速实现富文本组件(八)

一、技术点引入

前面七篇文章已经讲的差不多了,突然想起针对粘贴事件还没有谈谈解决方案,这篇文章就讲讲如何处理粘贴事件,以及自己对defineCustomElement实现富文本组件的看法。

粘贴事件中我们需要拦截一些粘贴的内容,或者是做一些内容上的替换;这里我以处理粘贴内容中携带的图片为例,探讨一下处理粘贴内容的方式

  1. 粘贴内容中的图片来源:复制的内容有可能是其他网页,这样图片的展示形式就是<img src='xxx'/>的形式;如果复制的内容来自于本地图片,则他就是一个file数组。
  2. 实现思路:在我们拿到粘贴内容的html后,我们可以直接进行字符串替换,将<img替换为<custom-img/img>替换为/custom-img>注意 ,这只是一个简单的思路,将会遇到很多bug,但是大致的方向是这样。
  3. custom-img自定义元素设计:由于采取直接替换的方式,所以一定要遵守以下原则:custom-img自定义元素传入的props键的名称一定要和img标签的属性名一致。例如:img标签有src属性,则custom-img元素也必须取这个名字,这样就能实现无感替换。

二、代码实现

首先是CustomImg.ce.vue自定义元素:

js 复制代码
<script setup lang="ts">
import { ref } from 'vue'

const props = withDefaults(
  defineProps<{ src?: string; file?: File; title?: string; alt?: string }>(),
  {
    src: '',
    title: '',
    alt: ''
  }
)

// 处理src和file两种形式的入参
const url = ref('')
if (props.src) {
  url.value = props.src
} else if (props.file) {
  try {
    const reader = new FileReader()
    reader.readAsDataURL(props.file)
    reader.onload = function () {
      if (reader.result) {
        url.value = reader.result
      }
    }
  } catch (e) {
    console.log(e)
  }
}

// 这个图片上传的函数就省略了
const uploadImg = ()=>{}

defineExpose({
  src: url,
  title: props.title,
  alt: props.alt
})
</script>
<template>
  <img
    class="image"
    crossorigin="anonymous"
    referrerpolicy="no-referrer"
    v-if="url"
    :src="url"
    :title="title"
    :alt="alt"
  />
</template>
<script lang="ts">
export default {
  name: 'custom-img',
  type: 'block'
}
</script>
<style lang="less">
.image {
  display: block;
  margin: 0 auto;
}
</style>

这个元素可以接受src或者file类型的图片类型,并做自动转换,上传接口这里就省略了,读者可以自行实现。

下面是监听粘贴事件,在RichText类的initRootNode方法中,加入监听: this.rootElement.addEventListener('paste', this.processingPasteEvt.bind(this)) 其中bind主要改变this的指向为当前RichText;

然后在RichText类中加入processingPasteEvt方法:

js 复制代码
  private processingPasteEvt(evt: ClipboardEvent) {
    if (!(evt.clipboardData && evt.clipboardData.items)) {
      return
    }

    const html = evt.clipboardData.getData('text/html')
    const text = evt.clipboardData.getData('text/plain')

    const range = this.getCursorRange()
    range.deleteContents()

    // 如果有html,则代表他是复制的页面,图片展示必定为标签的形式
    if (html) {
      const processedHtml = html.replace(/<img/g, '<custom-img').replace(/img>/g, 'custom-img>')
      // 这一句是判断html有没有变化,没有变化则代表没有图片,不用做后续操作,直接放行
      if (processedHtml !== html) {
        // 有图片则阻止默认操作
        evt.preventDefault()
        // 手动将html字符串转换为真实dom
        const parser = new DOMParser()
        const htmlDom = parser.parseFromString(processedHtml, 'text/html')
        const fragmentBody = htmlDom.querySelector('body')
        // 这里是只拿到真实dom的body内的元素,外层不需要
        if (fragmentBody) {
          const fragment = document.createDocumentFragment()
          while (fragmentBody.firstChild) {
            fragment.appendChild(fragmentBody.firstChild)
          }
          range.insertNode(fragment)
        } else {
          range.insertNode(htmlDom)
        }
      }
    } else if (!text) {
      // 这里是处理拿到的为文件数组
      evt.preventDefault()
      for (let i = 0, len = evt.clipboardData.items.length; i < len; i++) {
        const item = evt.clipboardData.items[i]
        // 判断是不是图片
        if (item.kind === 'file' && item.type.match('^image/')) {
          const pasteFile = item.getAsFile()
          // pasteFile就是获取到的文件
          const img = getCustomComponents()['custom-img']
          const imgInstance = new img.Constructor({ file: pasteFile })
          console.log(imgInstance)
          range.insertNode(imgInstance)
        }
      }
    }
  }

这个方法就能够处理所有的图片粘贴需求。

三、总结

这篇文章主要讲了在粘贴事件中如何用自定义元素来替换默认元素,最核心的点就是将自定义元素的入参设置的和默认元素的属性一模一样,这样就能单纯替换元素名而不用修改其他内容就能实现元素替换的需求。当然,这也是我的一种实现思路,可以参考借鉴,不一定是最好的。

四、写在最后

经过八篇文章的填坑,我们可以感受到在富文本中使用webcomponent目前还有许多的不足,许多坑点还不能完美的解决;同样对比将vue组件挂载到dom节点上,vue3中的createApp方法是将一个vue组件实例直接挂载到dom上,并且还能够避开webcomponent的各种坑,会不会采用这种方式会更好呢。

相关推荐
web147862107236 分钟前
C# .Net Web 路由相关配置
前端·c#·.net
m0_748247807 分钟前
Flutter Intl包使用指南:实现国际化和本地化
前端·javascript·flutter
飞的肖10 分钟前
前端使用 Element Plus架构vue3.0实现图片拖拉拽,后等比压缩,上传到Spring Boot后端
前端·spring boot·架构
青灯文案118 分钟前
前端 HTTP 请求由 Nginx 反向代理和 API 网关到后端服务的流程
前端·nginx·http
m0_7482548823 分钟前
DataX3.0+DataX-Web部署分布式可视化ETL系统
前端·分布式·etl
ZJ_.34 分钟前
WPSJS:让 WPS 办公与 JavaScript 完美联动
开发语言·前端·javascript·vscode·ecmascript·wps
GIS开发特训营39 分钟前
Vue零基础教程|从前端框架到GIS开发系列课程(七)响应式系统介绍
前端·vue.js·前端框架·gis开发·webgis·三维gis
Cachel wood1 小时前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
学代码的小前端1 小时前
0基础学前端-----CSS DAY9
前端·css
joan_851 小时前
layui表格templet图片渲染--模板字符串和字符串拼接
前端·javascript·layui