-
-
Notifications
You must be signed in to change notification settings - Fork 33.8k
Closed as not planned
Description
What problem does this feature solve?
I've created a <head>
management system and an awesome feature would be a native way to render VNodes to strings, both in SSR and on client-side
Here's what I've been currently doing in user-land:
hello-world.vue
<template>
<master>
<template slot="title">Hello World App</template>
<template slot="description">Meta description here</template>
<template slot="content">
Hello World
</template>
</master>
</template>
<script>
import master from '@/layouts/master'
export default {
components: {
master
}
}
</script>
master.vue
<template>
<servue>
<template slot="head">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ this.$slots.title ? `${ this.$slots.title[0].text } - My App`: `My App` }}</title>
<meta v-if="this.$slots.description" name="description" :content="this.$slots.description[0].text">
<slot name="head"/>
</template>
<template slot="content">
<slot name="content"/>
</template>
</servue>
</template>
<script>
import servue from './servue'
export default {
components: {
servue
}
}
</script>
You can probably see some issues with how this is done, it only accounts for a single text node, and there may be more. Plus, it seems hacky to directly access slot data inside a template
The head is currently being stringified by a small component:
servue.vue
<script>
const unaryTags = [
"area",
"base",
"br",
"col",
"embed",
"hr",
"img",
"input",
"keygen",
"link",
"meta",
"param",
"source",
"track",
"wbr"
]
function renderStartTag(VNode) {
let html = `<${VNode.tag}`
if (VNode.data) {
if (VNode.data.attrs) {
let attr = VNode.data.attrs
for (let name in attr) {
if (attr[name] === "") {
html += ` ${name}`
} else {
html += ` ${name}="${attr[name]}"`
}
}
}
}
return html + ">";
}
function isUnaryTag(VNode) {
return unaryTags.indexOf(VNode.tag) > -1
}
function getFullTag(VNode) {
if (!VNode.tag) return VNode.text
let html = renderStartTag(VNode)
if (VNode.children) {
html += getChildren(VNode)
}
if (!isUnaryTag(VNode)) {
html += `</${VNode.tag}>`
}
return html;
}
function getChildren(VNode) {
let html = ""
for (let i in VNode.children) {
let child = VNode.children[i]
html += getFullTag(child)
}
return html
}
export default {
created() {
let VNodes = this.$slots.head
let renderedHead = ""
for (let i in VNodes) {
let VNode = VNodes[i];
renderedHead += getFullTag(VNode)
}
if (this.$isServer) {
this.$ssrContext.head = `<!--VUESERVEHEAD START-->${renderedHead}<!--VUESERVEHEAD END-->`
}else{
let head = document.head
let node
let foundStart = false
let startNode
let children = head.childNodes
for(let node of children){
if(node.nodeType === Node.COMMENT_NODE){
if(node.nodeValue === "VUESERVEHEAD START"){
foundStart = true
startNode = node
continue
}
}
if(foundStart){
if(node.nodeType === Node.COMMENT_NODE){
if(node.nodeValue === "VUESERVEHEAD END"){
break
}
}
head.removeChild(node)
}
}
if(startNode){
let fakeMeta = document.createElement('meta')
startNode.after(fakeMeta)
fakeMeta.outerHTML = renderedHead
}
}
},
render(h){
return h('div', {
class: "servueWrapper"
}, this.$slots.content)
}
};
</script>
This whole process could be simplified by an API exposed by vue. The API already exists, it just needs to be exposed by Vue
What does the proposed API look like?
Vue.renderVNodesToString([VNode])
$vm.renderVNodesToString([VNode])
import { renderVNodesToString } from 'vue'
A few ideas
hybridwebdev, AlbertMarashi, vedmant, awefeng, leonardofed and 17 moreleonardofed, StreakyCobra and VitalickSleonardofed
Metadata
Metadata
Assignees
Labels
No labels