如何将 Vue VNode 渲染为字符串

Posted

技术标签:

【中文标题】如何将 Vue VNode 渲染为字符串【英文标题】:How to render a Vue VNode to a String 【发布时间】:2021-01-20 04:09:23 【问题描述】:

我正在尝试在我的 Vue 组件中使用 CSS 掩码。我需要完成下面toSvg函数的实现。这会将来自 this.$slots.default 的 Vue VNode 渲染为 SVG 字符串。

<script>
export default 
  computed: 
    maskImage() 
      const svg = this.toSvg(this.$slots.default);
      const encodedSvg = btoa(svg);
      return `url('data:image/svg+xml;base64,$encodedSvg')`;
    ,
  ,
  methods: 
    toSvg(vnode) 
      // TODO: How can I convert the VNode to a string like the one below?
      // In React, I could use const svg = ReactDOMServer.renderToStaticMarkup(vnode);
      return `<svg viewBox="0 0 260 68" xmlns="http://www.w3.org/2000/svg">
          <rect x="80" y="32"   rx="2" />
          <rect   rx="2" />
          <rect x="80" y="52"   rx="2" />
          <rect y="26"   rx="2" />
      </svg>`;
    ,
  ,
  render(createElement) 
    return createElement("div", 
      attrs: 
        class: "skeleton",
        style: `-webkit-mask-image: $this.maskImage; mask-image: $this.maskImage;`,
      ,
    );
  ,
;
</script>

<style lang="scss">
.skeleton 
  animation: skeleton-animation 2s infinite linear;
  background: linear-gradient(to right, hsl(30, 1, 99) 0%, hsl(30, 2, 95) 30%, hsl(30, 2, 95) 70%, hsl(30, 1, 99) 100%) 0 0 / 200% 100% hsl(30, 2, 95);
  overflow: hidden;
  position: relative;

  width: 200px;
  height: 100px;

  @keyframes skeleton-animation 
    100% 
      background-position: -200% 0;
    
  

</style>

使用示例:

<u-skeleton>
  <svg viewBox="0 0 260 68" xmlns="http://www.w3.org/2000/svg">
    <rect x="80" y="32"   rx="2" />
    <rect   rx="2" />
    <rect x="80" y="52"   rx="2" />
    <rect y="26"   rx="2" />
  </svg>
</u-skeleton>

显示为:

【问题讨论】:

【参考方案1】:

使用Vue.extend构造一个SVG组件构造函数,在构造函数的render function内部,渲染slots.default

下一步是创建它的实例,然后mount() 并获取编译后的 html

Vue.component('v-test', 
  computed: 
    maskImage() 
      let vnodes = this.$slots.default
      let SVGConstructor = Vue.extend(
        render: function (h, context) 
          return h('svg', 
            attrs: 
              xmlns: 'http://www.w3.org/2000/svg'
            
          , vnodes)
        
      )
      let instance = new SVGConstructor()
      instance.$mount()
      const encodedSvg = btoa(instance.$el.outerHTML);
      return `url('data:image/svg+xml;base64,$encodedSvg')`;
    
  ,
  render(createElement) 
    return createElement("div", 
      attrs: 
        class: "skeleton",
        style: `-webkit-mask-image: $this.maskImage; mask-image: $this.maskImage;`,
      ,
    )
  ,
)

new Vue(
  el: '#app'
)
.skeleton 
  animation: skeleton-animation 2s infinite linear;
  background: linear-gradient(to right, #fcfcfc 0%, #f3f2f2 30%, #f3f2f2 70%, #fcfcfc 100%) 0 0 / 200% 100% #f3f2f2;
  overflow: hidden;
  position: relative;
  width: 200px;
  height: 100px;

@keyframes skeleton-animation 
  100% 
    background-position: -200% 0;
  
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <v-test>
    <svg viewBox="0 0 260 68" xmlns="http://www.w3.org/2000/svg">
      <rect x="80" y="32"   rx="2" />
      <rect   rx="2" />
      <rect x="80" y="52"   rx="2" />
      <rect y="26"   rx="2" />
    </svg>
  </v-test>
  <hr>
  <v-test>
    <svg viewBox="0 0 260 68" x="0" y="0" xmlns="http://www.w3.org/2000/svg">
      <rect x="80" y="32"   rx="2" />
      <rect   rx="2" />
      <rect x="80" y="52"   rx="2" />
      <rect y="26"   rx="2" />
    </svg>
    <svg viewBox="0 0 260 68" x="20" y="-20" xmlns="http://www.w3.org/2000/svg">
      <rect x="80" y="32"   rx="2" />
      <rect   rx="2" />
      <rect x="80" y="52"   rx="2" />
      <rect y="26"   rx="2" />
    </svg>
  </v-test>
</div>

【讨论】:

以上是关于如何将 Vue VNode 渲染为字符串的主要内容,如果未能解决你的问题,请参考以下文章

你可以在 Vue 模板中渲染 VNode 吗?

Vue 页面渲染的流程

Vue Component

Vue中render渲染函数详解

vue的虚拟dom(Virtual DOM )

Vue源码后记-vFor列表渲染