v-html
的功能是替换元素的 innerHTML
,在大部分场景下都够用,但是因为是直接替换 innerHTML
所以无法用 Vue 的事件监听方法。下面引入一个需求,来实现这个功能。
需求
- 后端传来一串文本,我们需要把该文本中特定的字符替换成链接元素
- 在链接元素后面,加个搜索icon,用户点击该icon的时候执行某个预先定义的方法
第一个需求很容易搞定,思路如下:
- 用正则替换所有关键字位 a 元素
- v-html 把获取到的结果直接放到 DOM 里
.
但是前面说了,用v-html
无法实现事件监听,解决方法是使用渲染函数
。
实现功能
<script setup>
import { h, ref } from 'vue'
function searchText(text) {
alert(text)
}
function textWithLink({ text = '' }) {
const splitWord = '*#@$@#)()'
const patterns = [/(sm\d+)/g, /(av\d+|BV\w+)/g]
for(const reg of patterns) {
text = text.replace(reg, splitWord + '$1' + splitWord)
}
const words = text.split(splitWord).filter(word => word != '')
const children = []
for(const word of words) {
const isTarget = patterns.filter(pattern => pattern.test(word)).length > 0
if(isTarget) {
children.push(h('span', null, [
h('a', {href: 'https://t.me'}, word),
h('span', {
onclick: () => searchText(word)
}, 'search')
]))
} else {
children.push(word)
}
}
return h('pre', children)
}
const text = `
hello world
av1234
sm0000
BV0000
`
</script>
<template>
<textWithLink class="text" :text="text"/>
</template>
<style scoped>
.text{
white-space: pre-line;
}
</style>
思路是拆分关键字,然后对关键字的类型进行判断,如果是要转成链接的关键字,则用 h('a')
包裹,否则直接传入 h 函数作为文本元素。
补充
渲染函数和函数式组件
函数式组件实际类似 react 中的同名概念,这个组件没有 this,是无状态的(不会更改函数外部的状态,同样的输入,同样的输出)。
渲染函数就是 h 函数(?)。
VNode 可以直接在 template 中当组件使用。
如果你是 Vue2 版本,则渲染函数还有所不同,具体表现在
- 导入方式 - Vue2 h 是作为 render 函数的参数传入
- VNode 的属性设置
h('a', {onclick: xxx}, 'text')
在 vue3 中是可以直接使用的,而 vue2 中需要写成这样:h('a', {on: {onclick: xxx}}, 'text')
,具体规则可见官方文档