Vue - 从字符串中渲染一个元素
Posted
技术标签:
【中文标题】Vue - 从字符串中渲染一个元素【英文标题】:Vue - Render an element out of string 【发布时间】:2018-11-07 19:02:18 【问题描述】:我想从我的数据库中的字符串创建一个 vue 元素。
在这种情况下,它应该是带有笑脸表情的消息。
我实际上将它保存为:Some text with Emoji: :santa::skin-tone-3:
,并用<Emoji emoji=':santa::skin-tone-3:' :size='16' />
替换所有有效字符串在'::'之间
<template>
<span class=message v-html=convertedMessage></div>
</template>
<script>
import Emoji from 'emoji-mart-vue'
export default
components:
Emoji
,
computed:
convertedMessage()
return "Some text with Emoji: "+"<Emoji emoji=':santa::skin-tone-3:' :size='16' />"
</script>
但不是渲染的元素应该是这样的:
<span data-v-7f853594="" style="display: inline-block; width: 32px; height: 32px; background-image: url("https://unpkg.com/emoji-datasource-apple@4.0.4/img/apple/sheets/64.png"); background-size: 5200%; background-position: 15.6863% 41.1765%;"></span>
我只得到:
<emoji emoji=":santa::skin-tone-3:" :size="16"></emoji>
按预期渲染此元素的最佳可能性是什么?
【问题讨论】:
我可以推荐一个库:github.com/alexjoverm/v-runtime-template 是一个很好的抽象。 这能回答你的问题吗? How to use components in v-html 【参考方案1】:这里有一些更简单的方法来做你通常想做的事情。如果您提供更多细节,您的正确方向可能是这些解决方案之一之前的策略模式,但其中一个解决方案可能是您想要的:
1) Vue 允许您开箱即用地动态定义组件,所以这一行:
<component v-for="(component, index) in components" :key="'component'+index" :is="component.name" v-bind="component.props" />
...会在这样的对象数组中绘制一堆组件(例如):name: 'myComponentName', props: foo: 1, bar: 'baz'。
2) Vue 允许您通过简单地添加 v-html="variable" 来将 HTML 注入到组件中
例如,这里有一个创建动态 SVG 图标的组件,其中 SVG 的内容是从 javascript 变量动态注入的...
<template>
<svg xmlns="http://www.w3.org/2000/svg"
:
:
viewBox="0 0 18 18"
:aria-labelledby="name"
role="presentation"
>
<title :id="name" lang="en">name icon</title>
<g :fill="color" v-html="path">
</g>
</svg>
</template>
<script>
import icons from '../common/icons'
export default
props:
name:
type: String,
default: 'box'
,
width:
type: [Number, String],
default: 18
,
height:
type: [Number, String],
default: 18
,
color:
type: String,
default: 'currentColor'
,
data ()
return
path: icons[this.name]
,
created ()
console.log(icons)
</script>
<style scoped>
svg
display: inline-block;
vertical-align: baseline;
margin-bottom: -2px;
</style>
3) Vue 允许你通过 this.$options.template 动态定义你的组件模板:
export default
props: ['name', 'props'],
template: '',
created()
this.$options.template = `<component :is="name" $props.join(' ') ></component>`
,
4) Vue 允许您定义渲染函数,因此代理组件或其他高级恶作剧是微不足道的:
Vue.component('component-proxy',
props:
name:
type: String,
required: true
,
props:
type: Object,
default: () =>
,
render(h)
// Note the h function can render anything, like h('div') works too.
// the JS object that follows can contain anything like on, class, or more elements
return h(this.name,
attrs: this.props
);
);
一位聪明的天才在这里为此写了一个 jsbin:http://jsbin.com/fifatod/5/edit?html,js,output
5) Vue 允许您使用 Vue.extend 创建组件,甚至可以将原始 JavaScript 对象传递到页面或应用程序组件部分,就像这样,它从模板的简单字符串和一个简单的字符串创建一个名为“foo”的组件props 的数组,你也可以单独使用 JS 对象以同样的方式扩展数据、created、on 等:
new Vue(
el: '#app',
data:
foo: 'bar',
props: a: 'a', b: 'b'
,
components:
foo:
template: '<p> a b </p>',
props: ['a', 'b']
)
【讨论】:
尼克,你能给我一些关于将 vue 组件插入可编辑内容的建议吗?我应该将什么字符串存储到数据库中以重新创建用户对所见即所得 vue 编辑器的输入?这是我的问题***.com/questions/59849554/… 没问题! :) 回答并给了你一个示例代码笔来玩。 (codepen.io/njsteele/pen/wvBNYJY)。我还写了一个超级简单的 var 模板解析器,它可以让你注入在编辑器中更新的实时 vue 组件,并做其他很酷的事情 (github.com/onexdata/nano-var-template) 选择使用 var 模板路线将让你使用简码而不是 HTML 输出(即,如果您将编辑器内容存储在数据库中,它会更小,并且随着您更新组件而动态/更新)。如果您需要更多帮助,请告诉我。 哦,太好了!!!我明天一大早去看看。谢谢! 嘿伙计们,可以得到第 4 点)为 vue3 工作吗?我尝试使用相同的技术,但没有奏效。 jsbin.com/fifatod/5/edit?html,js,output【参考方案2】:我现在想到了什么:
convertedMessage()
let el = Vue.compile("<Emoji emoji=':santa::skin-tone-3:' :size='16' />")
el = new Vue(
components:
Emoji
,
render: el.render,
staticRenderFns: el.staticRenderFns
).$mount()
return "Some text with Emoji: "+el.$el.innerHTML
也许还有更好的解决方案来处理这个问题?
【讨论】:
您不想这样做有两个主要原因:首先,Vue.compile 会增加您的构建大小,因为它默认不包含在内,因为它被认为是构建时函数。其次,根据 VueJS 文档,在运行时编译“对于生产来说绝对是错误的”。原因是在运行时编译 Vue 组件是在 Vue 中渲染组件的最慢方法。 @NickSteele 那么如果我只能返回一个字符串而不是直接返回 html,我该怎么做呢?我在尝试做类似的事情时遇到了这个问题。 @NicoBleiler 你的意思是“我只能返回一个字符串”,就像从一个函数中一样?我不确定我是否遵循您的问题,但我在这里对 Vue 让您呈现组件的方式的回答几乎涵盖了您可以构建动态 Vue 组件的所有方式:***.com/a/53752539/3196360 @jacob-graf,你找到更好的解决方案了吗?【参考方案3】:当我需要做类似的事情时,这就是我的做法。
我正常渲染了组件,比如 <my-component>
,但由于我只需要渲染的 HTML,所以我将它包装在 <div class="hidden">
中,如下所示:
<div class="hidden">
<my-component />
</div>
使用 CSS:
.hidden
display: none;
这样,我可以通过$refs
引用元素,或者您可以使用document.querySelector()
从DOM 中获取元素,同时保持它对最终用户不可见。
所以在上面的例子中,要获得渲染的 HTML,你只需要这样做:
let el = document.querySelector('.hidden');
let renderedHTMLString = el.children[0].outerHTML;
通过这种方式,您可以获得呈现的 HTML,而无需任何与 Vue.compile
或任何其他插件相关的开销成本。 正常渲染。把它藏起来。访问它的外层HTML。
【讨论】:
有时无法预先加载。例如,如果您通过 ajax 接收 HTML。【参考方案4】:v-html
仅呈现纯 HTML,请参阅 https://vuejs.org/v2/guide/syntax.html#Raw-HTML
在您的情况下,您可能应该看看渲染函数和 JSX。我不是专家,但似乎有些人正在使用 dangerouslySetInnerHTML
JSX 函数实现您想要的。看看这个线程:How do I convert a string to jsx?
我知道有时我们别无选择,但如果可以的话,我认为最好的解决方案是避免从后端生成模板,因为它破坏了关注点分离(也可能是安全问题)。
【讨论】:
以上是关于Vue - 从字符串中渲染一个元素的主要内容,如果未能解决你的问题,请参考以下文章
vue从后台获取的数据有html标签通过v-html渲染到页面,然后怎么给这里面的html添加样式?