如何在Vue3中的运行时将组件呈现为其HTML字符串



我正在Vue 3中开发一个动态面板,用户可以在其中为他们想要渲染的部分指定简单的类似XML的模板。

我希望XML中与特定Vue组件名称匹配的节点被呈现为Vue组件,而其他节点应该被呈现为普通HTML。

例如,XML模板可能如下所示:

<Dashboard>
<Elements>
<div class="title">Switches</div>
<Switches device="kitchen-bulb" />
</Elements>
</Dashboard>

<Elements>内部的<div>应该呈现为普通HTML(我已经通过利用<slot>和将节点内容传递给<Elements>组件上的v-html实现了这一点(,而与特定导入的Vue组件的名称匹配的<Switches>应该呈现为Vue组件-在本例中,使用从模板传递的device属性。

现在的代码是这样的:

/// index.js
import Switch from './Switch'
export default {
Switch,
}
/// Elements.vue
<template>
<div class="elements">
<div class="container" v-html="content" />
</div>
</template>
<script>
import elements from './index'
export default {
name: "Elements",
props: {
content: {
type: String,
},
},
mounted() {
const content = new DOMParser()
.parseFromString(`<div>${this.content}</div>`, 'text/html')
.childNodes[0]
Object.entries(elements).forEach(([name, element]) => {
this.$options.components[name] = element
const nodes = content.getElementsByTagName(name);
[...nodes].forEach((node) => {
const renderedComponent = magicStringToRenderedHTMLStringOrDOM(element, node)
node.parentElement.replaceChild(renderedComponent, node)
})
})
this.content = content.childNodes[0].innerHTML
},
}
</script>

content<Elements>的父级传递给组件的节点内容。

我需要的是一些东西来填充magicStringToRenderedHTMLStringOrDOM调用,在给定Vue组件定义、节点表示和属性的情况下,渲染Vue组件,我可以简单地在content中替换它,并用v-html渲染它。

我知道Vue 2曾经为Vue.compile提供过类似的功能。我发现的唯一一个Vue 3函数是createRenderer,但我还没有找到很多如何使用它的例子

我已经解决了这个问题,方法是使用本机render函数,并为与索引上的动态组件匹配的每个标记动态安装一个应用程序。

比从头开始编译和渲染模板来重新发明轮子更好:(

/// Content of index.js
import Switch from './Switch'
export default {
Switch,
}
/// Content of Elements.vue
<template>
<div class="elements">
<div class="container" ref="container" />
</div>
</template>
<script>
import elements from './index'
export default {
name: "Elements",
props: {
content: {
/**
* Example content property:
* <div>
*   <h1>Switches</h1>
*   <Switches />
* </div>
*/
type: String,
},
},
mounted() {
this.$refs.container.innerHTML = this.content
// Iterate over all the dynamic components on index.js
Object.entries(elements).forEach(([name, element]) => {
// Dynamically load each of them
this.$options.components[name] = element;
// Get all the tags on the content DOM that match
// a certain dynamic component
[...this.$refs.container.getElementsByTagName(name)].forEach((component) => {
// Replace each of them with a new Vue app
// that renders the component and mounts it
// on the DOM node
createApp({
render() { return h(element) }
}).mount(component)
})
})
}
}
</script>

最新更新