Step1. 使用vue-server-renderer
生成html
片段
vue
官方提供了vue-server-renderer
这个库,它能将vue
实例初步渲染,生成html,效果如下:
const Vue = require('vue')
const renderer = require('vue-server-renderer').createRenderer()
const app = new Vue({
template: '<div>Hello {{name}}!</div>',
data() {
return {
name: 'world'
}
}
})
renderer.renderToString(app, (err, html) => {
if(err) throw err
console.log(html)
})
image.png
注意到vue将data应用到template上,渲染出html。
线上代码: https://github.com/boomler/vue-ssr-demo/tree/master/ssr01
Step2: 使用html模板和Express
上一步只生成了html
片段,但是不能生成完整的HTML文档,下面我们想办法生成一个完整的html
并能处理请求。
新建index.template.html
,内容如下:
<html lang="en">
<head>
<title>hello</title>
</head>
<body>
<h1>Message from US: </h1>
<!--vue-ssr-outlet-->
</body>
</html>
文件结构
这次我们在createRenderer方法中传入template字段,为ssr指定一个html模板:
// index.js
const fs = require('fs');
const Vue = require('vue');
const server = require('express')();
const renderer = require('vue-server-renderer').createRenderer({
template: fs.readFileSync(__dirname + '/index.template.html', 'utf-8')
});
server.get('*', (req, res) => {
const app = new Vue({
template: `<div>Hello {{name}}!</div>`,
data: {
name: 'World'
}
});
renderer.renderToString(app, (err, html) => {
if (err) {
res.status(500).end('Internal Server Error')
return
}
res.end(html)
});
});
server.listen(8000, () => {
console.log('listening on 8000')
});
vue-ssr-outlet会被替换成生成的HTML
这个时候我们就完成了第一步,初次访问页面的时候返回渲染好的html
线上代码: https://github.com/boomler/vue-ssr-demo/tree/master/ssr02
Step 3 根据url返回相应HTML
这一部分讨论如何使用vue-router
假设我们正在做一个博客网站,我们有三个页面:
- 文章列表(
/articles
) - 文章详情 (
/article-detail
) - 个人页面(
/me
)
那么我们访问 XXX.com/articles我们希望拿到渲染好的文章列表页面,访问/me拿到个人信息页面。我们怎么根据路由生成特定的HTML?
使用vue-router
先增加一个路由文件:
// router.js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export function createRouter() {
return new Router({
mode: 'history',
routes: [
{path: '/', component: () => import('./components/Article-list.vue')},
{path: '/articles/:id', component: () => import('./components/Article-detail.vue')}
]
})
}
根据context.url去做路由切换,然后返回切换之后的app
// entry-server.js
const {createApp} =require("./app");
module.exports = context => {
return new Promise((resolve, reject) => {
const {app, router} = createApp()
router.push(context.url) //根据context.url去做路由切换,然后返回切换之后的app
router.onReady(() => {
const matchedComponents = router.getMatchedComponents()
if (!matchedComponents.length) {
return reject({code: 404})
}
resolve(app)
}, reject)
})
}
拿到请求之后,将请求的url
添加到context
中,传给createApp()
方法
// server.js
const path = require('path')
const express = require('express')
const createApp = require(path.resolve(__dirname, './dist/main.js'))
const template = require('fs').readFileSync(__dirname + '/index.template.html', 'utf-8');
const app = express()
const renderer = require('vue-server-renderer').createRenderer({
template
})
app.get('*', (req, res) => {
const context = {url: req.url} //将请求的url添加到context中,传给createApp
createApp(context).then(app => {
renderer.renderToString(app, (err, html) => {
if (err) {
res.status(500).end(JSON.stringify(err))
return
}
res.end(html)
})
})
})
app.listen(8000)
使用webpack打包,生成能在Node环境运行的bundle
我们发现我们现在有.vue
文件,有import
语句,这些都不能在node环境中运行,于是万能的webpack登场了。我们需要webpack做以下事情:
- 使用
vue-loader
将vue
转成js
- 将
import/export
转换成require / module.exports
这样我们才能在node服务器上直接调用vue的代码
// build/server-build.js
const path = require('path')
const nodeExternals = require('webpack-node-externals')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
// 将 entry 指向应用程序的 server entry 文件
entry: path.resolve(__dirname, '../entry-server.js'),
// 这允许 webpack 以 Node 适用方式(Node-appropriate fashion)处理动态导入(dynamic import),
// 并且还会在编译 Vue 组件时,
// 告知 `vue-loader` 输送面向服务器代码(server-oriented code)。
target: 'node',
// 对 bundle renderer 提供 source map 支持
devtool: 'source-map',
// 此处告知 server bundle 使用 Node 风格导出模块(Node-style exports)
output: {
path: path.resolve(__dirname, '..', './dist'),
libraryTarget: 'commonjs2'
},
// https://webpack.js.org/configuration/externals/#function
// https://github.com/liady/webpack-node-externals
// 外置化应用程序依赖模块。可以使服务器构建速度更快,
// 并生成较小的 bundle 文件。
externals: nodeExternals({
whitelist: /\.css$/
}),
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
}]
},
// 这是将服务器的整个输出
plugins: [
new VueLoaderPlugin()
]
}
下面是我们的博客系统的结构图
系统结构图我们为server端写一份打包配置文件,利用webpack打包成能在node
环境运行的代码。
当有请求到达node server时,node server使用server-bundle做服务器端渲染,生成html字符串作为response
线上代码: https://github.com/boomler/vue-ssr-demo/tree/master/ssr03
网友评论