我有一个巨大的动态HTML字符串,正在将其加载到Vue组件中的div
中。 HTML字符串本质上是WYSIWYG编辑器的内容。最初,我只是为此使用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文档中找不到任何内容。
有人知道这是否可能吗?谢谢。
答案 0 :(得分:4)
您可以使用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>
此外,您还可以使用此命令将prop /事件处理程序传递到字符串中的组件:
<!-- 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
来访问道具,因为它们不在props
组件的dynamic-html
定义中)
完整代码示例:
// 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>
答案 1 :(得分:1)
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');