ZEROMAKE | keep codeing and thinking!
2017-02-19 | vue

vue-ssr-koa2

前言

  • 在之前的vue-ssr中我是使用 express 来做 ssr 服务器+api 服务器。
  • 但是有些时候可能我希望换一个 web 框架。
  • 所以就有了这篇闲的慌系类。
  • vue-ssr 中使用 koa2 来替代 express

一.更换依赖包

  1. 删掉 express 及它的中间组件 [‘compression’, ‘express’, ‘serve-favicon’]
  2. 添加 koa2 及它的一些中间组件 [‘koa2’, ‘[email protected]’, ‘koa-send’]

二.修改 server.js

1
// [L3-L5] 替换引入的包
2
const Koa = require('koa2')
3
const KoaRuoter = require('koa-router')
4
const KoaServe = require('./build/serve')
5
// /build/serve.js 这个是我手写的一个静态文件挂载中间组件
6
7
// [L14-L17] 声明创建app和路由对象
8
const app = new Koa()
9
const router = new KoaRuoter()
10
// dev下因为使用原express中间组件,
11
// 一定要在设置setup-dev-server之前设置api路由。
12
// 包括普通的静态json文件
13
const api_router = new KoaRuoter()
14
15
// [L20-L44] 更改 serve 函数以便支持koa2,替换原有serve使用的地方
16
const serve = (url, path, cache) => KoaServe(url, {
17
root: resolve(path),
18
maxAge: cache && isProd ? 60 * 60 * 24 * 30 : 0
19
})
20
21
// 模拟api
22
app.use(serve('/api/topstories.json', './public/api/topstories.json'))
23
app.use(serve('/api/newstories.json', './public/api/newstories.json'))
24
// 修改为koa支持
25
api_router.get('/api/item/:id.json', (ctx, next) => {
26
const id = ctx.params.id
27
const time = parseInt(Math.random()*(1487396700-1400000000+1)+1400000000)
28
const item = {
29
by: "zero" + id,
30
descendants: 0,
31
id: id,
32
score: id - 13664000,
33
time: time,
34
title: `测试Item:${id} - ${time}`,
35
type: 'story',
36
url: `/api/item/${id}.json`
37
38
}
39
ctx.body = item
40
})
41
app.use(api_router.routes()).use(api_router.allowedMethods())
42
43
// [L89-L132] ssr的处理转换换为koa2
44
// 原express 调用res.end会通知express结束,
45
// koa2则需要使用返回一个Promise来达到异步
46
router.get('*', (ctx, next) => {
47
if (!renderer) {
48
return ctx.body ='waiting for compilation.. refresh in a moment.'
49
}
50
ctx.set("Context-Type", "text/html")
51
ctx.set("Server", serverInfo)
52
const s = Date.now()
53
const context = { url: ctx.url }
54
const renderStream = renderer.renderToStream(context)
55
const res = ctx.res
56
return new Promise(function(resolve){
57
renderStream.once('data', () => {
58
// 不设置这个会默认404
59
res.statusCode = 200
60
res.write(indexHTML.head)
61
})
62
renderStream.on('data', chunk => {
63
res.write(chunk)
64
})
65
renderStream.on('end', () => {
66
if (context.initialState) {
67
res.write(
68
`<script>window.__INSTAL_STATE__=${
69
serialize(context.initialState)
70
}</script>`
71
)
72
}
73
res.end(indexHTML.tail)
74
resolve() // 通知koa2异步操作已经结束
75
console.log(`whole request: ${Date.now() - s}ms`)
76
})
77
renderStream.on('error', err => {
78
if (err && err.code === '404') {
79
res.statusCode = 404
80
res.end('404 | Page Not Found')
81
resolve() // 通知koa2异步操作已经结束
82
return
83
}
84
res.statusCode = 500
85
res.end('Internal Error 500')
86
resolve() // 通知koa2异步操作已经结束
87
console.error(`error during render : ${ctx.url}`)
88
console.error(err)
89
})
90
// [L134] 注册路由
91
app.use(router.routes()).use(router.allowedMethods())

三.手写一个用于 koa2 静态文件中间组件 build/serve.js

1
"use strict";
2
// 使用 koa-send 模块来发送文件
3
const send = require("koa-send");
4
// 导出一个参数为url-path + koa-send-opt的函数
5
module.exports = function serve(path, opt) {
6
// 设置了参数后返回一个标准koa2中间组件方法
7
return function(ctx, next) {
8
const pathUrl = ctx.path;
9
// 只有匹配了path且请求方法为 get,head 才进行发送文件
10
if (
11
(ctx.method == "HEAD" || ctx.method == "GET") &&
12
pathUrl.startsWith(path)
13
) {
14
let newOpt = null;
15
let newPath = null;
16
// 处理 path = pathUrl 且结尾不为/的特殊情况
17
if (pathUrl === path) {
18
if (!path.endsWith("/")) {
19
// 去除root路径中的path文件
20
// 但为未处理url的文件名与真实文件名不同
21
const newRoot = opt.root.slice(0, -path.length);
22
newOpt = Object.assign({}, opt, { root: newRoot });
23
newPath = pathUrl;
24
}
25
} else {
26
// 去除多余的url
27
newPath = pathUrl.slice(path.length);
28
}
29
// 通过 koa-send 发送
30
return send(ctx, newPath || "/", newOpt || opt);
31
}
32
return next();
33
};
34
};

四.使用现成的 npm 包来修改 build/setup-dev-server.js

  • git 地址
  • 安装npm i koa2-webpack-middleware-zm -D
1
// [L6]
2
const {
3
koaDevMiddleware,
4
koaHotMiddleware
5
} = require("koa2-webpack-middleware-zm");
6
// [L25]
7
app.use(koaDevMiddleware(devMiddleware));
8
// [L39-L40]
9
const expressHotMiddleware = require("webpack-hot-middleware")(clientCompiler);
10
app.use(koaHotMiddleware(expressHotMiddleware));
  • 是不是发觉特别简单好吧其实这个 npm 包是我自己写的
  • 在下一篇博文再介绍吧,这篇博文代码还是太多

五.下篇博文预告

  1. 修改博文书写方式,尽量放片段代码加图片文字来说明
  2. 代码量多的部分使用 github 的 url 来引用
  3. 下篇博文应该是 npm 包制作及 github 集成测试。还有 koa 的中间组件

六.源码

源码在这里