【问题标题】:How can I add a Vue component within some HTML to another Vue component?如何将某个 HTML 中的 Vue 组件添加到另一个 Vue 组件?
【发布时间】:2019-12-08 04:51:43
【问题描述】:

我有一个巨大的动态 HTML 字符串,我将其加载到 Vue 组件内的 div 中。 HTML 字符串本质上是来自所见即所得编辑器的内容。本来我只是用v-html来做这个的,没问题。

但是,现在有些情况下,我需要将部分 HTML 字符串替换为实际的 Vue 组件,而我不确定最好的方法。

例如,我可能在 HTML 字符串中有一些如下所示的标记:

||map:23||

而我想要做的是用如下的 Vue 组件替换它:

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

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

然后我尝试为 HTML 内容使用一个插槽,这确实有效,但它具有令人讨厌的副作用,即在 Vue 能够正确呈现之前在屏幕上显示一堆未格式化的 HTML 内容一两秒钟.

所以我的问题是:还有另一种(更优雅的)方法可以做到这一点吗?我在想,在 Vue 组件加载 HTML 内容后,我可以以某种方式在标记中找到 ||map:23|| 实例,然后用正确的 Vue 组件动态替换它们,但如果可能的话,我不会知识;我在 Vue 文档中找不到任何内容。

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

【问题讨论】:

    标签: javascript html laravel vue.js vuejs2


    【解决方案1】:

    您可以使用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>

    【讨论】:

    • 我以为我非常了解 Vue。我对编译方法存在的线索为零。已更新。
    【解决方案2】:

    Turtlefight 的回答非常有用且完整,但对于任何寻求快速、简单答案的人,请使用文字 component 组件和 :is 如下将包含 Vue 组件的 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');
    

    【讨论】:

      猜你喜欢
      • 2020-07-02
      • 2021-02-14
      • 2021-08-07
      • 1970-01-01
      • 2019-05-15
      • 1970-01-01
      • 2019-02-20
      • 2019-07-03
      • 2020-03-23
      相关资源
      最近更新 更多