javascript - 如何在运行时在 Vue 3 中将组件呈现为其 HTML 字符串

标签 javascript vue.js vuejs3

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

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

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

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

<div> <Elements> 内部应该呈现为普通 HTML(我已经通过利用 <slot> 并将节点内容传递给 v-html 组件上的 <Elements> 实现了这一点),而 <Switches> ,与特定导入的 Vue 组件的名称相匹配,应呈现为 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 组件定义、节点表示和属性,呈现我可以在 content 中简单替换的 Vue 组件并用 v-html 渲染.

我知道 Vue 2 曾经提供 something similar with Vue.compile .我发现显然做类似事情的唯一 Vue 3 函数是 createRenderer ,但我还没有找到很多如何使用它的例子。

最佳答案

我已经通过使用 native 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>

关于javascript - 如何在运行时在 Vue 3 中将组件呈现为其 HTML 字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66717511/

相关文章:

javascript - Google Closure Linter 在 Windows 的 Sublime Text 2 中不起作用

javascript - 替换字符串中的最后一个单词

javascript - VueJS - 列出数组并独立显示/隐藏

html - 如何在 Vue 路由器中定义要在组件内部使用的变量?

typescript - 如何在开发模式下使用 typescript 在 vue 3 中启用 devtools

javascript - Vue 3 ref 未更新为模板上的组件

javascript - 获取div相对于窗口的绝对位置

javascript - 为什么在这种情况下 "this"是全局/窗口对象?

javascript - 如何使用 v-select 列表显示 vuetify 数据表的隐藏列?

laravel - 在 node_modules 文件夹中找不到模块(但文件存在)