Servue
Server-side rendering engine that renders vue files into html strings
Install / Use
/learn @futureaus/ServueREADME
Servue
Helping you serve vue with servue
Server-side rendering engine that renders vue files into html strings
- Servue
- Installation
- What is servue?
- Setup
- Layouts & Views
- Setting custom path variables
- Custom html body template
- Custom Preprocessor/Language Support
- Mode
- Head Management
- Passing data to vue from server-side
- Global State / Cross Component Data
- Precompiling Vue Pages
- Future Of This Package
- Package Changelog / Version
Installation
npm install servue --save
Then install peer dependencies (so you can control your vue version)
npm install vue --save
npm install vue-server-renderer --save
npm install vue-template-compiler --save
What is servue?
Servue server-side-renders Vue Single File Components (.vue files) into html. It is a fully capable templating engine, and allows users to create nested layouts using slots and components.
The renderer provides the correct scripts, and styles on the client-side. It creates no extra build/compiled files. It's perfect for multi-page applications and great for users wanting to use Vue with Express or Koa
It's this easy to render .vue files:
await servue.render('home') // renders "home.vue" into html string
Requires Node 8+
Features
- [x] Supports precompilation
- [x] Supports layouts
- [x] Supports templating
- [x] Supports server-side Rendering
- [x] Supports head management
- [x] Supports imports CSS files and other assets
- [x] Supports custom language preprocessors (less, pug, etc)
- [x] No build or compile files, all in-memory
- [x] Supports custom templating
Setup
Express Usage
const Servue = require("servue")
const express = require("express")
var app = express()
var servue = new Servue()
servue.resources = path.resolve(__dirname, "resources")
app.get('/', async (req, res) => {
res.send(await servue.render('home')) // renders "./resources/home.vue"
})
app.listen(2000)
Koa Usage
const Servue = require("servue")
const Koa = require("koa")
var app = new Koa()
var servue = new Servue()
servue.resources = path.resolve(__dirname, "resources")
app.use(async (ctx) => {
ctx.body = await servue.render('home') // renders "./resources/home.vue"
})
app.listen(2000)
Layouts & Views
Servue fully supports templating features and allows for multiple and nested layouts using Vue slots.
Here's a simple example:
Ensure you wrap your app/root-level layout with
<servue>. It is required for Servue to work.
layouts/parent.vue
<template>
<servue>
<template slot="head">
<title><slot name="title"/></title>
</template>
<template slot="content">
<h1>The page title is: <slot name="title"></slot></h1>
<slot name="content"/>
</template>
</servue>
</template>
This layout has a slot for content named content and a slot for the title named title
Now, the home file can use this layout:
pages/home.vue
<template>
<parent>
<template slot="title">Home</template>
<template slot="content">
Hello
</template>
</parent>
</template>
<script>
import parent from "layouts/parent.vue"
export default {
components: {
parent //layouts can be nested, just provide the correct slots
}
}
</script>
Rendered Result
<html>
<head>
<title>Home</title>
<!-- CSS, state and head are all automatically injected here -->
</head>
<body>
<div id="app">
<h1>The page title is: Home</h1>
Hello
</div>
<script>/** Vue client-side webpack bundle script is rendered here **/</script>
</body>
</html>
Setting custom path variables
You may use your own custom paths for folders
//Sets views folder path which contains .vue .css .less .js and etc
servue.resources = path.resolve(__dirname, "resources")
//path of node_modules
servue.nodemodules = path.resolve(__dirname, 'node_modules')
Custom html body template
servue.template = (content, context, bundle) => (`
<!DOCTYPE html>
<html${ context.htmlattr ? ' ' + context.htmlattr : '' }>
<head>
${context.head ? context.head : ''}
${context.renderResourceHints()}
${context.renderStyles()}
${context.renderState({windowKey: '__INITIAL_STATE__', contextKey: "data"})}
${context.renderState({windowKey: '__INITIAL_ROOTSTATE__', contextKey: "state"})}
</head>
<body>
${content}
<script>${bundle.client}</script>
</body>
</html>
`)
Custom Preprocessor/Language Support
Here we add support for the LESS/SCSS/Stylus/pug preprocessor language so it can be used in our .vue files.
In this example, we use the stylus-loader packagetop to add stylus support to our package.
servue.webpackCommon.module.rules.push({
test: /\.styl(us)?$/,
use: [
'vue-style-loader',
'css-loader',
'stylus-loader'
]
})
<template>
<div class="red">
This will be red
</div>
</template>
<style lang="red">
.red{
color: red;
}
</style>
Mode
For faster compilation, you can use the production version of vue.
servue.mode = "production" //default = "development"
However, this will remove vue development warnings, so it is best to use development for debugging & dev as it enables vue dev-tools.
Head Management
layouts/parent.vue
<template>
<servue>
<template slot="head">
<title><slot name="title"/></title>
<meta name="og:title" content="This is my Facebook OpenGraph title">
<slot name="head"/> <!-- Pass slot for head in-case other child vues want to add additional elements to the head -->
</template>
<template slot="content">
<h1>Page: <slot name="title"/></h1>
<slot name="content"/>
</template>
</servue>
</template>
the head template slot is rendered into the <head> tags on the server-side. The head tag can be nested, as shown in parent.vue. This means that home.vue may optionally pass it's own head to the user.
home.vue
<template>
<parent>
<template slot="title">Home</template>
<template slot="content">
Hello
</template>
<template slot="head">
<meta name="meta2">
</template>
</parent>
</template>
<script>
import parent from "layouts/parent.vue"
export default {
components: {
parent //layouts can be nested, just provide the correct slots
}
}
</script>
head output
<head>
<title>Home</title>
<meta name="meta1">
<meta name="meta2">
<!-- Other Servue injections here -->
</head>
Passing data to vue from server-side
Via asyncData() in .vue file
asyncData() is a custom feature provided by servue, it allows you to make asynchronous requests like API calls. It is called on the server-side.
Data returned from asyncData is merged into the data() function, and then also passed onto the client.
It only works on the rendered file, and not inside subcomponents or layouts.
router.get((ctx) => {
ctx.body = await servue.render('pages/home', {ctx})
})
pages/home.vue
export default {
async asyncData(ssrContext){
let ctx = ssrContext.ctx
return {
url: ctx.url,
hello: "World"
}
}
}
Via data
You can simply do this through the context argument
let data = { "hello": "world" }
await servue.render('home', {data})
This data is merged with your vue component's data function (if there is one), and can then be accessed
