-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ssr] Add Vue function to render VNode to html string #9205
Comments
Technically, you should be able to do this by using a function that returns the vnode and render using what Vue already exports (https://ssr.vuejs.org/guide/#rendering-a-vue-instance). |
the issue is, the <head>
{{{ head }}}
{{{ renderResourceHints() }}}
{{{ renderStyles() }}}
</head> Note |
If I had access to the proposed function, I would be able to pass it to the server easily via the export default {
created: function(){
if(this.$isServer){
var VNodeArray = this.$slots.head
var html = ""
for(let i in VNodeArray){
html += Vue.renderVNode(VNodeArray[i])
}
this.$ssrContext.head = html
}
}
}
|
And I understand the use-case may be rare, but this is something that can and will increase server-side adoption and would be even useful for Nuxt. This would finally allow a head management system to exist in the html/template section of SFCs and would make SSR, SEO and head management much easier If this is not possible, do you know a way to find a solution to this issue? |
@posva if it cannot be added to the core, there is an alternate place it could go: https://ssr.vuejs.org/guide/build-config.html#client-config scroll to there should be a This would allow: createBundleRenderer(serverBundle, {
inject: false,
template: `<!DOCTYPE html>
<html>
<head>
{{{ renderVNodesToString(headVNodes) }}}
{{{ renderResourceHints() }}}
{{{ renderStyles() }}}
</head>
<body>
<!--vue-ssr-outlet-->
<script>${ clientBundle }</script>
</body>
</html>`
}) where headVNodes is set by |
I'd be happy to implement an API with tests in Proposed API const { createBundleRenderer, renderVNodesToString } = require("vue-server-renderer")
var renderer = createBundleRenderer(serverBundle, {
inject: false,
template: `<!DOCTYPE html>
<html>
<head>
{{{ renderVNodesToString(headVNodes) }}}
</head>
<body>
<!--vue-ssr-outlet-->
</body>
</html>`
})
// headVNodes set by this.$ssrContext = this.$slots.head in vue file
renderer.renderToString({
renderVNodesToString
}) This would allow for a far greater head management system for Vue that's based in the |
My further findings show that if the function is provided, it would need to be either:
|
@yyx990803 I would be happy to try implement this. My implementation would involve making ssr |
Accidental close |
@yyx990803 I think that it was accidentally moved from Todo to Done in 2.6 project, once @DominusVilicus accidentally closed the issue. I'm sorry if bothering. |
I think I originally misunderstood the request when I added it to 2.6 - after looking at it in more details I think this can be done in userland. There are a few things that I have concern about landing this in Vue itself:
The use case also seems niche, so I think we'd be better off to test an implementation in userland first. |
I'd be happy to implement this in userland, but the issue is that there is no exposed API that I could use to render VNodes to a string The reason you can't use const { createBundleRenderer} = require("vue-server-renderer")
var renderer = createBundleRenderer(serverBundle, {
inject: false,
template: `<!DOCTYPE html>
<html>
<head>
{{{ renderVNodes(headVNodes) }}}
</head>
<body>
<!--vue-ssr-outlet-->
</body>
</html>`
})
async function renderVNodes(vnodes){
// create bundle renderer for the head
// await renderToString
// return rendered string
// error because template can't handle promise values
}
//pass $ssrContext the VNode renderer
renderer.renderToString({
renderVNodes
}) |
From studying the internals, it seems the two options are:
The simplest option would be rewriting the template compiler for the |
I think this overcomplicates the problem. (Adding API surface for a niche case) It's quite straightforward if what you need is just sync rendering of head elements (with a very predictable range of element/attributes, so very few edge cases to watch out for). VNodes are just objects in the shape of |
You're right, I will probably just do that. But I still actually like the function-based template.
With template function const renderer = createBundleRenderer(bundle.server, {
async template(result, context){
return `
<!DOCTYPE html>
<html${ context.htmlattr ? ' ' + context.htmlattr : '' }>
<head>
${ await context.renderVNodes(context.head) }
${ context.renderResourceHints() }
${ context.renderStyles() }
${ context.renderState({ windowKey: '__INITIAL_STATE__', contextKey: "data"}) }
</head>
<body>
${result}
<script>${ bundle.client }</script>
</body>
</html>`
}
}) vs string template const renderer = createBundleRenderer(bundle.server, {
inject: false,
template: `<!DOCTYPE html>
<html{{{ htmlattr ? ' ' + htmlattr : '' }}}>
<head>
{{{ head }}}
{{{ renderResourceHints() }}}
{{{ renderStyles() }}}
{{{ renderState({ windowKey: '__INITIAL_STATE__', contextKey: "data"}) }}}
</head>
<body>
<!--vue-ssr-outlet-->
<script>${ bundle.client }</script>
</body>
</html>`
}) |
You're right. The usage example makes it much clearer. Let's consider adding this to 2.6. |
Awesome! I managed to create my own custom VNode renderer for the specific task of head management (you can see it in #code-review in Vue Land) I did make a working version with the function based template in #9324 but tests were failing for some reason. It was a non-breaking change too (users could optionally still use the string template) |
For me the |
Finally a working draft:
and inside a component:
Now need to populte this with API data. |
Have you had a look at https://github.com/futureaus/servue? |
@DominusVilicus I have the following task and am wondering if you are trying to accomplish something similar with this request. Environment: Task:
Reading up on https://github.com/futureaus/servue now to see if this is a viable solution, but wanted to toss a note out to you for your feedback. |
@alucidwolf What are you trying to accomplish? You shouldn't be rendering HTML code from a server, if you need a dynamic menu, send the menu data as the data, and let your .vue files render it |
@DominusVilicus I want to create different page layouts from this Html so it is not just data I need for the menu but also the Html structure for the page layout, which can be different. |
What problem does this feature solve?
Use case:
I'm developing a Server-Side Renderer for Vue (which works with Express, Koa & etc. Will increase migration to Vue). For the SSR's head management to work, it needs a stable API to render
VNode
s to text.The way my Vue SSR package will function:
master.vue
home.vue
My goal is getting
home.vue
'shead
slot rendered into a string and injecting it into thethis.$ssrContext
so it can be read and injected on the server-sidein
master.vue
, I can accessthis.$slots.head
with no issue, and it contains the correctVNode
smy question is, how can I render them into a string? a way to basically do:
From my research, I have been unable to find an easy way to do this.
To understand how the renderer works
This is the code for the serverbundlerenderer
What does the proposed API look like?
The text was updated successfully, but these errors were encountered: