今天是我入职次日,leader跟我说,昨天配置好了服务端渲染的文件,今天就先研究研究如何使用koa来搭建一个node server吧!前端
按照惯例,我去koa官网查了一下什么是koa,结果官网很简单的一句话介绍:koa--基于node.js平台的下一代web开发框架。vue
我的感受koa官方文档对于前端小白来讲,写的不是很友好,建议上手以前先看看阮一峰的koa框架教程和廖雪峰写的关于koa入门文章。node
而后引入项目第一步,安装koa:webpack
npm i koa -S
复制代码
安装完以后,首先在项目根目录下新建一个server文件夹,而后在此文件夹下新建一个server.js文件,而后在里面引入koa:ios
const Koa = require('koa')
const app = new Koa()
const isDev = process.env.NODE_ENV === 'development'
复制代码
这里为何要声明isDev呢?由于服务端渲染是分开发环境和生产环境两种不一样的状况。web
而后咱们继续在server.js里面先写一个中间件来记录全部的请求和抓取的错误,这样能够很好的了解到在服务端渲染的过程当中是否出现了一些错误,并及时排查掉错误。npm
先撸为敬:json
app.use(async (ctx, next) => {
try {
console.log(`request with path ${ctx.path}`)
await next()
} catch (err) {
console.log(err)
ctx.status = 500
if (isDev) {
ctx.body = err.message
} else {
ctx.body = 'please try again later'
}
}
})
复制代码
简单解释一下:在函数前面加一个async,就表明异步处理函数,而参数next表示执行下一个 异步处理的函数。在try循环体内,console打印出请求的路径。若是是isDev为true的状况,能够直接将错误信息写到body里面,这样就能够在页面上直接看到错误信息。若是不是开发环境,能够写一个友善的提醒文字,例如:“please try again later”。axios
这就是最简单的一个koa中间件,用来记录全部的请求及出现的错误,而且返回一个错误信息。浏览器
接下来,聊一聊如何处理服务端渲染。
在处理服务端渲染以前,首先要在terminal里面安装一下koa-router:
npm i koa-router -S
复制代码
这是koa提供的一个路由的工具。而后在server文件夹下面新建一个routers文件夹,紧接着在里面新建两个文件,一个是dev-ssr.js,另外一个是ssr.js。前者是处理开发时服务端渲染的状况,后者是处理正式环境下的状况。
在dev-ssr.js文件中,首先要引入koa-router:
const Router = require('koa-router')
复制代码
在这里,还须要使用到两个工具,须要安装下:
npm i axios -S
复制代码
npm i memory-fs -D
复制代码
在node端发送请求的axios,固然也能够在浏览器端发送请求。在安装的时候记住后面跟的是-S
,由于在业务代码中能够用到。
而memory-fs只有在开发的时候才会用到,因此后面跟的是-D
。可能有童鞋要问了,这个memory-fs是用来干吗的?别急,闰土给你们截一张官网图片看看便一目了然了:
大意是:一个简单的内存文件系统。将数据保存在JavaScript对象中。
而后,话很少说,先把这两个工具引入进来:
const axios = require('axios')
const MemoryFS = require('memory-fs')
复制代码
紧接着,再来引入两个工具:
const webpack = require('webpack')
const VueServerRenderer = require('vue-server-renderer')
复制代码
由于要在node开发环境中打包代码,而且须要服务端渲染。
接下来,要引入serverConfig,就是入职第一天写的那个配置文件webpack.config.server.js:
const serverConfig = require('../../build/webpack.config.server')
复制代码
而后,如何能在node开发环境中让webpack跑起来呢? 答案是经过serverCompiler:
const serverCompiler = webpack(serverConfig)
复制代码
而后去new一个mfs实例:
const mfs = new MemoryFS()
serverCompiler.outputFileSystem = mfs
复制代码
这样就指定了webpack的输出目录在MemoryFS里面。
有了这些配置以后,再去声明一个bundle:
let bundle
复制代码
用来记录webpack每次打包生成的新的文件。
serverCompiler.watch({}, (err, stats) => {
if (err) throw err
stats = stats.toJson()
stats.erros.forEach(err => console.log(err))
stats.hasWarnings.forEach(warn => console.warn(err))
const bundlePath = path.join(
serverConfig.output.path,
'vue-ssr-server-bundle.json'
)
bundle = JSON.parse(mfs.readFileSync(bundlePath, 'utf-8'))
})
复制代码
这里使用watch()的好处是:跟使用webpack-dev-server同样,在client目录下每次修改一个文件,它都会从新执行一次打包,而后就能够拿到新的文件了。
serverCompiler.watch()的第一个参数是空对象,第二个参数是一个回调。若是有err直接抛出。
而后stats这块我感受有点晦涩难懂,leader告诉我说,先照着作,而后有空再去看webpack的文档。
接下来就能够读取生成的bundle文件了,拼接读取文件的路径,设置文件名字,而且制定编码为utf-8,最后经过JSON.parse()将字符串转成JSON。
执行完以上步骤以后,就能够将内容返回给HTML了。
在服务端渲染期间,使用ejs模板引擎生成HTML。经过VueServerRenderer的createBundleRenderer()方法帮助生成一个能够直接调用renderer的函数。在这里面接收几个参数,第一个是inject,设置为false,这样它就不会执行其余的注入的操做了。第二个是clientManifest,它会自动生成一个带有script标签的js文件引用的字符串,这样能够直接添加到ejs的内容里面。
最后,dev-ssr.js的完整代码以下:
const Router = require('koa-router')
const axios = require('axios')
const path = require('path')
const fs = require('fs')
const MemoryFS = require('memory-fs')
const webpack = require('webpack')
const VueServerRenderer = require('vue-server-renderer')
const serverConfig = require('../../build/webpack.config.server')
const serverCompiler = webpack(serverConfig)
const mfs = new MemoryFS()
serverCompiler.outputFileSystem = mfs
let bundle
serverCompiler.watch({}, (err, stats) => {
if (err) throw err
stats = stats.toJson()
stats.erros.forEach(err => console.log(err))
stats.hasWarnings.forEach(warn => console.warn(err))
const bundlePath = path.join(
serverConfig.output.path,
'vue-ssr-server-bundle.json'
)
bundle = JSON.parse(mfs.readFileSync(bundlePath, 'utf-8'))
})
const handleSSR = async (ctx) => {
if (bundle) {
ctx.body = 'wait a moment...'
return
}
const clientManifestResp = await axios.get(
'http://127.0.0.1:8080/vue-ssr-client-manifest.json'
)
const clientManifest = clientManifestResp.data
const template = fs.readFileSync(
path.join(__dirname, '../server.template.ejs')
)
const renderer = VueServerRenderer
.createBundleRenderer(bundle, {
inject: false,
clientManifest
})
}
复制代码
此次使用koa搭建node server的体验只是聊到了renderer这一步,后面我会继续聊聊如何把bundle渲染成实际的HTML内容,并把它添加到template里面。最新的文章都会第一时间更新在个人公众号<闰土大叔>里面,欢迎关注。