v-html 的功能是替换元素的 innerHTML,在大部分场景下都够用,但是因为是直接替换 innerHTML 所以无法用 Vue 的事件监听方法。下面引入一个需求,来实现这个功能。
第一个需求很容易搞定,思路如下:
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 版本,则渲染函数还有所不同,具体表现在
h('a', {onclick: xxx}, 'text') 在 vue3 中是可以直接使用的,而 vue2 中需要写成这样: h('a', {on: {onclick: xxx}}, 'text') ,具体规则可见官方文档