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(&quot;https://unpkg.com/emoji-datasource-apple@4.0.4/img/apple/sheets/64.png&quot;); 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】:

当我需要做类似的事情时,这就是我的做法。

我正常渲染了组件,比如 &lt;my-component&gt;,但由于我只需要渲染的 HTML,所以我将它包装在 &lt;div class="hidden"&gt; 中,如下所示:

<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添加样式?

vue.js条件渲染

vue中,我从后台取出数据渲染vue组件,我数据渲染出来了,但是页面有报错,是啥原因

vue为啥要求组件模板只能有一个根元素

Vue实例的属性及模板渲染

Vue模板渲染的原理是啥