vue3 把富文本中的图片替换为 el-image
有时候我们需要对富文本中的图片、视频或者其他元素添加更加丰富的功能,比如在图片上增加el-image 的预览功能。
你可能会想去操作 DOM, 然后为图片写一些自己的方法来实现预览功能,这个还不算太难,但是如果想把 html video 标签替换为自己封装的 VideoPlayer 组件呢? 这好像又麻烦了一点。
其实,vue 提供了渲染函数,通过h()、render()可以用少量的代码,非常方便的把元素替换为 Element Plus 的组件,或者是自己封装的组件。
在此,我们尝试把 img
元素 替换为 el-image
渲染函数
在开始之前,需要了解 vue3 的 渲染函数 cn.vuejs.org/guide/extra…
Vue 提供了一个 h()
函数用于创建 vnodes,它有点像操作 DOM 的 createElement,不过h()函数创建的是 vnodes,虚拟 DOM 最终通过渲染器构建成真实的 DOM 树。
import { h } from 'vue'
const vnode = {
type: 'div',
props: {
id: 'hello'
},
children: [
/* 更多 vnode */
]
}
如果我们用JS 去操作真实 DOM,要写的东西还挺多(回忆一下 jQuery),但是用h()函数,在渲染 Vue 组件上这件事上,几句话就可以实现。不管是什么类型的文件,只要从中导入的是有效的 Vue 组件,h
就能正常运作。
render()函数,就是帮我们把 h()函数创建的 vnodes 渲染到 DOM 上的关键。
具体实现
- 找到指定范围内的 img 元素们
- 遍历 img 节点数组
- 在 当前img 附近创建一个新 div(或者你喜欢的其他HTML节点),使用h()函数生成 ELImage 的vnode,然后使用render()函数塞进刚才创建的 div,
- 删掉原来的 img 元素
h(ElImage, {...})
可以加上你想要的 el-image 的属性
直接贴代码
<template>
<div class="m-4">
<div class="mb-4">替换富文本的内容为 Vue 组件</div>
<div>
<el-input
type="textarea"
v-model="content"
autosize="{minRows:10, maxRows:20}"
disabled
></el-input>
</div>
<div class="rich-content" v-html="content"></div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, h, render } from 'vue'
import { ElImage } from 'element-plus'
const content = ref(`
<div>
<p>海洋 1</p>
<img src="https://images.pexels.com/photos/189349/pexels-photo-189349.jpeg" width="800"/>
<p>海洋 2</p>
<img src="https://images.pexels.com/photos/1001682/pexels-photo-1001682.jpeg" width="800"/>
<p>海洋 3</p>
<img src="https://images.pexels.com/photos/189349/pexels-photo-189349.jpeg" width="800"/>
</div>
`)
onMounted(() => {
const els = document
.querySelector('.rich-content')
?.querySelectorAll('img') as NodeListOf<HTMLImageElement>
if (!els || els.length === 0) return
els?.forEach((el) => {
const divElement = document.createElement('div')
el.after(divElement)
const imgSrc = el.getAttribute('src')
if (imgSrc) {
const vNode = h(ElImage, {
src: imgSrc,
style: { width: el.getAttribute('width') + 'px' },
lazy: true,
previewSrcList: [imgSrc]
})
render(vNode, divElement)
el.remove()
}
})
})
</script>
这个需求的实现确实不难,重点是理解 Vue 的渲染机制,可以灵活地使用渲染函数实现更加丰富的功能。
有任何问题或者兴趣也可以在公众号:自由前端之路 找到我。
转载自:https://juejin.cn/post/7393191747930685490