Awesome
ssr
Riot module to render riot components on the server
Installation
npm i -S riot @riotjs/compiler @riotjs/ssr
Usage
render - to render only markup
You can simply render your components' markup as it follows:
import MyComponent from './my-component.js'
import render from '@riotjs/ssr'
const html = render('my-component', MyComponent, { some: 'initial props' })
Important If you want to import raw .riot
components in your application you might want to use @riotjs/register
Note that components rendered on the server will always automatically receive the isServer=true
property.
renderAsync - to handle asynchronous rendering
Components that can not be rendered synchronously must expose the onAsyncRendering
method to the renderAsync
function. For example:
<async-component>
<p>{ state.username }<p>
<script>
export default {
onBeforeMount({ isServer }) {
// if it's not SSR we load the user data right the way
if (!isServer) {
this.loadUser()
}
},
loadUser() {
return fetch('/user/name').then(({name}) => {
this.update({ name })
})
},
// this function will be automatically called only
// if the component is rendered via `renderAsync`
onAsyncRendering() {
return this.loadUser()
}
}
</script>
</async-component>
The above component can be rendered on the server as it follows:
import MyComponent from './async-component.js'
import { renderAsync } from '@riotjs/ssr'
renderAsync('async-component', MyComponent, { some: 'initial props' }).then(
(html) => {
console.log(html)
},
)
Notice that the onAsyncRendering
can either return a promise or use the resolve, reject callbacks:
export default {
// this is ok
async onAsyncRendering() {
await loadData()
},
}
export default {
// this is also ok
onAsyncRendering(resolve, reject) {
setTimeout(resolve, 1000)
},
}
IMPORTANT nested onAsyncRendering
on children components are not supported!
fragments - to render html and css
You can also extract the rendered html
and css
separately using the fragments
function:
import MyComponent from './my-component.js'
import { fragments } from '@riotjs/ssr'
const { html, css } = fragments('my-component', MyComponent, {
some: 'initial props',
})
renderAsyncFragments - to handle asynchronous fragments rendering
It works like the method above but asynchronously
Advanced tips
If you want to render your whole document you can simply pass html
as name of your root node. For example
<html>
<head>
<title>{ state.message }</title>
<meta each={ meta in state.meta } {...meta}/>
</head>
<body>
<p>{ state.message }</p>
<script src='path/to/a/script.js'></script>
</body>
<script>
export default {
state: {
message: 'hello',
meta: [{
name: 'description',
content: 'a description'
}]
}
}
</script>
</html>
It can be rendered as it follows:
import MyRootApplication from './my-root-application.js'
import render from '@riotjs/ssr'
const html = render('html', MyRootApplication)
Better SSR control using the createRenderer
For a better control over your HTML rendering you might want to use the createRenderer
factory function.
This method allows the creation of a rendering function receiving the {getHTML, css, dispose, element}
option object.
getHTML
: give you the rendered html of your component as stringcss
: the css of your component as stringdispose
: clean the memory used on the server needed to render your componentelement
: the component instance you are mounting
For example
import MyComponent from './my-component.js'
import { createRenderer } from '@riotjs/ssr'
const logRendrer = createRenderer(({ getHTML, getCSS, dispose, component }) => {
const html = getHTML()
const css = getCSS()
console.log('Rendering the component: %s', component.name)
dispose()
return { html, css }
})
// use your logRenderer
const { html, css } = logRendrer('my-component', MyComponent, {
some: 'initial props',
})
DOM Globals
@riotjs/ssr
needs DOM globals (like window
, document
...) to properly render your markup.
With the domGlobals
exported object you can decide manually when the globals should be created and deleted from in your node applications.
import { domGlobals } from '@riotjs/ssr'
domGlobals.create()
// global DOM object in your node environement are now defined
console.log(global.window, global.document)
// they will be cleared and will be undefined
domGlobals.clear()
Caveat
If you are rendering your whole HTML you will not be able to use multiple times the inline <script>
<style>
tags.
Of course, you can use only once the ones used by Riot.js to customize your components. For example:
<html>
<head>
<!-- allowed -->
<script src='path/to/some/script.js'></script>
<!-- not allowed -->
<style>
</style>
<!-- not allowed -->
<script>
const globalstuff = {}
</script>
</head>
<body>
<!-- application html -->
</body>
<!-- allowed -->
<script>
export default {
// app code
}
</script>
<!-- allowed -->
<style>
:host {}
</style>
</html>