如何将 Vue VNode 渲染为字符串 [英] How to render a Vue VNode to a String

查看:76
本文介绍了如何将 Vue VNode 渲染为字符串的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

I'm trying to use a CSS mask in my Vue component. I need to complete the implementation of the toSvg function below. This will render the Vue VNode coming from this.$slots.default to an SVG string.

<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" width="160" height="12" rx="2" />
          <rect width="180" height="20" rx="2" />
          <rect x="80" y="52" width="95" height="12" rx="2" />
          <rect y="26" width="68" height="42" 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>

Usage example:

<u-skeleton>
  <svg viewBox="0 0 260 68" xmlns="http://www.w3.org/2000/svg">
    <rect x="80" y="32" width="160" height="12" rx="2" />
    <rect width="180" height="20" rx="2" />
    <rect x="80" y="52" width="95" height="12" rx="2" />
    <rect y="26" width="68" height="42" rx="2" />
  </svg>
</u-skeleton>

Shows as:

解决方案

Uses Vue.extend to construct one SVG component constructor, inside render function of the constructor, it renders slots.default.

Next step is create its instance, then mount() and get the compiled 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" width="160" height="12" rx="2" />
      <rect width="180" height="20" rx="2" />
      <rect x="80" y="52" width="95" height="12" rx="2" />
      <rect y="26" width="68" height="42" 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" width="160" height="12" rx="2" />
      <rect width="180" height="20" rx="2" />
      <rect x="80" y="52" width="95" height="12" rx="2" />
      <rect y="26" width="68" height="42" 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" width="160" height="12" rx="2" />
      <rect width="180" height="20" rx="2" />
      <rect x="80" y="52" width="95" height="12" rx="2" />
      <rect y="26" width="68" height="42" rx="2" />
    </svg>
  </v-test>
</div>

这篇关于如何将 Vue VNode 渲染为字符串的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆