如何将 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>
相关文章