如何在一个Vue组件中添加另一个Vue组件的HTML部分?

5
我有一个庞大而动态的HTML字符串,我需要将其加载到Vue组件中的
中。该HTML字符串基本上是来自所见即所得编辑器的内容。最初,我只使用v-html进行此操作,效果还不错。
然而,现在出现了一些情况,我需要用Vue组件实际替换HTML字符串的一部分,但我不确定最佳方法是什么。
例如,我可能在HTML字符串中有一些标记,看起来像以下内容:
||map:23||

我想做的是将其替换为一个Vue组件,如下所示:

<map-component :id="23"></map-component>

我试着在Laravel中提前进行字符串转换,然后在Vue组件中使用v-html注入内容,但这似乎不能加载Vue组件。

然后我尝试使用HTML内容的插槽,这样可以工作,但有一个讨厌的副作用,在Vue能够正确呈现之前,屏幕上会显示一堆未经格式化的HTML内容,持续一两秒钟。

所以我的问题是:是否有另一种(更简洁)的方法来做到这一点?我想,在Vue组件加载HTML内容后,我可以在标记中找到例如||map:23||的实例,然后动态地将其替换为正确的Vue组件,但如果可以的话,我不知道如何操作;我在Vue文档中找不到任何信息。

有人知道是否可能吗?谢谢。

2个回答

6
您可以使用Vue.compile来编译一个包含Vue组件的模板字符串。然后,您可以将其与具有render()方法的组件结合使用,以仅呈现模板。
// this component will dynamically compile the html
// and use it as template for this component.
Vue.component("dynamic-html", {
  props: ["html"],
  computed: {
    template() {
      if(this.html)
        return Vue.compile(this.html).render;
      return null;
    }
  },
  render() {
    if(this.template)
      return this.template();
    return null;
  }
});

这使您能够渲染任意模板字符串,其中也可以包含Vue组件:

<dynamic-html html="<some-component></some-component>">
</dynamic-html>

此外,您还可以使用这个功能将属性/事件处理程序传递给字符串中的组件:

<!-- Passing down props -->
<dynamic-html
    html='<some-component :prop="$attrs.myprop"></some-component>'
    :myprop="12"
></dynamic-html>

<!-- passing down events -->
<dynamic-html
    html='<some-component @click="$emit('foo', $event)"></some-component>'
    @foo="doSomething"
></dynamic-html>

您需要使用$attrs来访问属性,因为它们不在dynamic-html组件的props定义中。

完整的代码示例:

// this component will dynamically compile the html
// into a vue component
Vue.component("dynamic-html", {
  props: ["html"],
  computed: {
    template() {
      if(this.html)
        return Vue.compile(this.html).render;
      return null;
    }
  },
  render() {
    if(this.template)
      return this.template();
    return null;
  }
});

Vue.component("red-bold-text", {
  props: ["text"],
  template: '<span class="red">{{text}}</span>'
});

new Vue({
  el: '#root',
  data: {
    html: null,
    myBoundVar: "this is bound from the parent component"
  },
  mounted() {
    // get the html from somewhere...
    setTimeout(() => {
      this.html = `
        <div>
          WELCOME!
          <red-bold-text text="awesome text"></red-bold-text>
          <red-bold-text :text="$attrs.bound"></red-bold-text>
          <button @click="$emit('buttonclick', $event)">CLICK ME</button>
        </div>
      `;
    }, 1000);
  },
  methods: {
    onClick(ev) {
      console.log("You clicked me!");
    }
  }
});
.red { color: red; font-weight: bold; margin: 6px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="root">
  <div>This will load dynamically:</div>
  <dynamic-html :html="html" :bound="myBoundVar" @buttonclick="onClick"></dynamic-html>
</div>


3
我本以为我对Vue很了解,却不知道还有compile方法存在。点赞了。 - maiorano84

5
海龟战斗的回答非常有帮助和完整,但对于任何寻求快速简单答案的人,可以使用字面意义上的component组件和:is来注入包含Vue组件的HTML内容到动态组件中。请保留HTML标签。
// htmlStrWithVueComponents is a large string containing HTML and Vue components.
<component
    :is="{
        template: `<div>${htmlStrWithVueComponents}</div>`
    }"
>
</component>

以下是一些描述这种技术的来源:


编辑:值得注意的是,component :is 在所能做的方面相当有限。您无法创建非常复杂的template字符串或添加mounted方法等。

对于我的特定用例,因为我需要一些更复杂的东西,最终我选择了以下方法,这是简单答案和Turtlefight答案之间的混合:

// This code goes within the parent component's mounted method or wherever is makes sense:
Vue.component('component-name-here', {
    // Can add much more complex template strings here.
    template: `
        <div class="someClass">
            ${content}
        </div>
    `,
    // Can add lifecycle hooks, methods, computed properties, etc.
    mounted: () => {
        // Code here
    }
});

const res = Vue.compile(`
    <component-name-here>
    </component-name-here>
`);

new Vue({
    render: res.render,
    staticRenderFns: res.staticRenderFns
}).$mount('dom-selector-for-dom-element-to-be-replaced-by-vue-component');

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接