如何将某个 HTML 中的 Vue 组件添加到另一个 Vue 组件?
Posted
技术标签:
【中文标题】如何将某个 HTML 中的 Vue 组件添加到另一个 Vue 组件?【英文标题】:How can I add a Vue component within some HTML to another Vue component? 【发布时间】: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 文档中找不到任何内容。
有人知道这是否可能吗?谢谢。
【问题讨论】:
【参考方案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>
这里有几个描述这种技术的资料:
https://jannerantala.com/tutorials/vue-replace-text-with-component-with-props/ https://jsfiddle.net/Herteby/5kucj6ht/编辑:值得注意的是,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');
【讨论】:
以上是关于如何将某个 HTML 中的 Vue 组件添加到另一个 Vue 组件?的主要内容,如果未能解决你的问题,请参考以下文章
如何通过使用 Vue.js 中的路由器概念单击按钮来简单地从一个组件(主页)导航到另一个组件