【问题标题】:How to render a component to its HTML string at runtime in Vue 3如何在 Vue 3 运行时将组件渲染为其 HTML 字符串
【发布时间】:2021-06-17 09:55:14
【问题描述】:

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

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

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

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

&lt;Elements&gt; 内部的 &lt;div&gt; 应该呈现为普通 HTML(我已经通过利用 &lt;slot&gt; 并将节点内容传递给 &lt;Elements&gt; 组件上的 v-html 实现了这一点),而 @ 987654329@ 与特定导入的 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&lt;Elements&gt;的节点内容,由其父级传递给组件。

我需要的是填充 magicStringToRenderedHTMLStringOrDOM 调用的东西,给定 Vue 组件定义、节点表示和属性,呈现 Vue 组件,我可以简单地在 content 中替换并使用 v-html 呈现。

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

【问题讨论】:

    标签: javascript vue.js vuejs3


    【解决方案1】:

    我通过使用原生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>
    

    【讨论】:

      猜你喜欢
      • 2021-09-19
      • 2022-12-20
      • 2021-01-20
      • 2021-12-09
      • 2019-03-26
      • 2018-12-03
      • 2022-10-06
      • 2020-03-14
      • 1970-01-01
      相关资源
      最近更新 更多