SkillAgentSearch skills...

Servue

Server-side rendering engine that renders vue files into html strings

Install / Use

/learn @futureaus/Servue
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Servue

Helping you serve vue with servue

Build Status Codacy Badge Coverage percentage Node Minimum Version NPM version NPM Downloads Last Commit

Server-side rendering engine that renders vue files into html strings

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

View on GitHub
GitHub Stars60
CategoryDevelopment
Updated1y ago
Forks3

Languages

JavaScript

Security Score

65/100

Audited on Nov 1, 2024

No findings