Vue如何实现插槽下渲染dom字符串使用

寻技术 VUE 2023年10月22日 94

本篇内容介绍了“Vue如何实现插槽下渲染dom字符串使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

需求

先来简单介绍下需求:这是在开发一个低代码平台的时候所遇到的需求,用户可以自己写一些组件上传到平台,在使用的时候可以对组件的

props
slots
events
进行配置,这就涉及到了动态插槽内容的实现了。对于代码编辑器的实现使用了 code-mirror,感兴趣的可以去看下,这里就不多说了。这里主要讲如何实现动态插槽内容渲染。

先来大致看下代码的上下文:

<template>
    <Component
        v-bind="componentProps"
    >
        <template
            v-for="item of componentSlots"
            #[item[0]]
        >
            
        </template>
    </Component>
</template>

<script setup lang="ts">
    const Component = defineAsyncComponent({
        // ...
    })
    const componentProps = ref({})
    const componentSlots = ref<[string, string][]>([])
    
    onMounted(async () => {
        componentProps.value = await loadProps()
        componentSlots.value = await loadSlots()
    })
</script>

v-html

说到渲染

dom
字符串,那
v-html
肯定是首要想到的。但是
template
标签上是无法使用
v-html
的,那么只能在
template
下写一个普通元素来塞
dom
字符串,代码如下:
<template
    v-for="item of componentSlots"
    #[item[0]]
>
    <span v-html="item[1]">
    </span>
</template>

这样的确实现了动态渲染插槽内容的需求,但是多出一个标签总是感觉不太妥当;而且也很难保证这个多出的

span
不会对组件的布局产生影响。这让我又陷入的沉思...

v-outerHTML

既然

innerHTML
不完全满足需求,那么使用
outerHTML
不就完美解决这个问题了吗?于是我去查关于
vue
如何使用
outerHTML
相关资料,发现并没有很好的案例,那就自己实现一个吧。
export const vOuterHTML = {
    bind(el, binding) {
        el.outerHTML = binding.value
    },
    update(el, binding) {
        el.outerHTML = binding.value
    },
}
<template
    v-for="item of componentSlots"
    #[item[0]]
>
    <span v-outerHTML="item[1]">
    </span>
</template>

代码保存,页面一刷新,这不完美实现了吗?但是等我去编辑

dom
字符串并保存后发现问题了,组件渲染内容并没有改变,控制台也报错了。

什么原因呢?原来是因为在

update
阶段时,
span
已不在页面中,因此无法对他执行
outerHTML
赋值。

那怎么办呢?只需要在

bind
阶段记住
span
的父节点,然后在更新阶段手动再创建一个
span
append
到父节点下,再进行 outerHTML赋值即可,代码如下:
export const vOuterHTML = (() => {
    let parentNode = null
    
    return {
        bind(el, binding) {
            parentNode = el.parentNode
            el.outerHTML = binding.value
        },
        update(el, binding) {
            if (parentNode) {
                const span = document.createElement('span')
                parentNode.appendChild(span)
                span.outerHTML = binding.value
            }
        },
        unbind() {
            if (parentNode) {
                parentNode = null
            }
        }
    }
})()
关闭

用微信“扫一扫”