一、安装
npm install koa-generator -g
Koa2 koa2-test
npm install & npm run dev
二 、package.js 的配置
1、安装npm i cross-env --save-dev
2、package.js 的配置 添加 cross-env NODE_ENV=dev
"dev": "cross-env NODE_ENV=dev ./node_modules/.bin/nodemon bin/www",
"prd": "cross-env NODE_ENV=production pm2 start bin/www",
三、安装koa-redis koa-generic-session redis 设置session
app.js相关的配置
const Koa = require('koa')
const app = new Koa()
const views = require('koa-views')
const json = require('koa-json') // post data中 json格式的处理
const onerror = require('koa-onerror')
const bodyparser = require('koa-bodyparser')
const logger = require('koa-logger')
const session = require('koa-generic-session')
const redisStore = require('koa-redis')
const index = require('./routes/index')
const users = require('./routes/users')
const blog = require('./routes/blog')
const user = require('./routes/user')
// error handler
onerror(app) // app检测 (error)检测
// middlewares 应用中间件
app.use(bodyparser({ // 处理post data上传数据的 接收很多个格式
enableTypes:['json', 'form', 'text']
}))
app.use(json()) // 接收到很多格式之后处理成json格式
app.use(logger()) // 日志
app.use(require('koa-static')(__dirname + '/public'))
app.use(views(__dirname + '/views', {
extension: 'pug'
}))
// logger 看当时服务请求的耗时
app.use(async (ctx, next) => {
const start = new Date() // 记录数据来时的时间
await next() // 再去执行其他的操作 //中间这个所花费的耗时
const ms = new Date() - start // 再用当前的时候减去记录的时间
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`)
})
//async返回的是一个promise对象 next是下一个中间件的函数,下个中间件也是一个异步函数格式,执行这个也是返回一个promise对象,有了await后后面加的也是个promise,这样把异步变成同步的写法
// session配置
app.keys = ['WJoli#$_23312_']
app.use(session({
// 配置cookie
cookie: {
path: '/',
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000
},
// 配置redis
store: redisStore({
// all: '127.0.0.1:6379' //先写死本地redis端口
all: `${REDIS_CONF.host}: ${REDIS_CONF.port}`
})
}))
// routes
app.use(index.routes(), index.allowedMethods())
app.use(users.routes(), users.allowedMethods())
app.use(blog.routes(), blog.allowedMethods())
app.use(user.routes(), user.allowedMethods())
// error-handling
app.on('error', (err, ctx) => {
console.error('server error', err, ctx)
});
module.exports = app
日志
安装koa-morgan
创建文件夹logs 再在此文件下创建access.log
在app.js中添加以下代码
// 引用
const path = require('path')
const fs = require ('fs')
const morgan = require('koa-morgan')
// 使用
const ENV = process.env.NODE_ENV
if(ENV !== 'production') {
// 开发环境/测试环境
app.use(morgan('dev'))
} else {
// 线上环境
const logFileName = path.join(__dirname, 'logs', 'access.log')
const writeStream = fs.createWriteStream(logFileName, {
flags: 'a'
})
app.use(morgan('combined', {
stream: writeStream // morgan 也是给予stream方式来写的,stream是写入文件提供性能很好的方式
}))
}
如何启用线上环境查看日志
"dev": "cross-env NODE_ENV=dev ./node_modules/.bin/nodemon bin/www",
"prd": "cross-env NODE_ENV=production ./node_modules/.bin/nodemon bin/www",
npm run prd
koa2 中间件原理
分析
- app.use 用来注册中间件,先收集起来
- 实现next机制,即上一个通过next触发下一个
- 不涉及method和path的判断 因为koa里不涉及到路由就不会有get post url path
洋葱圈模型
const Koa = require('koa');
const app = new Koa();
// 1 先执行logger中 的 await next()
// 2 再执行 x-response-time 中的 const start = Date.now()
// 3 再执行 x-response-time 中的 await next();
// 4 再去response 中的 ctx.body = 'Hello World';
// 5 再执行 x-response-time 中 const ms = Date.now() - start; ctx.set('X-Response-Time', `${ms}ms`);
// 6 再执行 logger 中 const rt = ctx.response.get('X-Response-Time'); 最后再打印出来
// logger 记录日志
app.use(async (ctx, next) => {
console.log('第一层洋葱 -开始')
await next();
const rt = ctx.response.get('X-Response-Time'); //获取ctx刚才设置的时间差
console.log(`${ctx.method} ${ctx.url} - ${rt}`); //最后打印出来
console.log('第一层洋葱 -开始')
});
// x-response-time 记录请求时间
app.use(async (ctx, next) => {
console.log('第二层洋葱 -开始')
const start = Date.now(); // 记录时间
await next();
const ms = Date.now() - start; //获取一个时间差
ctx.set('X-Response-Time', `${ms}ms`); //将时间差设置到ctx中
console.log('第二层洋葱 -结束')
});
// response
app.use(async ctx => {
console.log('第三层洋葱 -开始')
ctx.body = 'Hello World';
console.log('第三层洋葱 -结束')
});
app.listen(5000);
打印出来的结
koa2的中间件代码
const http = require ('http')
// 函数,组合中间件
function compose (middlewareList) { //传入中间的列表
return function (ctx) { //返回一个函数,这个函数接受一个ctx的参数
//中间件调用的逻辑
function dispatch (i) {
const fn = middlewareList[i]
try {
return Promise.resolve( //中间件执行封装成一个promise不管中间件写的是不是一个async函数都封装 成一个promise (书写格式的兼容处理)
fn(ctx, dispatch.bind(null, i + 1)) //中间件的执行 next的机制动过dispatch.bind(null, i+1)来实现next的机制
)
} catch (err) {
return Promise.reject(err)
}
}
return dispatch(0)
}
}
class LikeKoa2 {
constructor () {
this.middlewareList = [] //中间件要存储的地方
}
use (fn) {
this.middlewareList.push(fn) // 中间件push到fn中
return this
}
createContext(req, res) { //将req,res进行组合组合成ctx就可以
const ctx = {
req,
res
}
ctx.query = req.query
return ctx
}
handleRequest (ctx, fn) {
return fn(ctx)
}
callback() {
const fn = compose(this.middlewareList) //有了compose之后获取第一个中间件然后去执行
return (req, res) => {
const ctx = this.createContext(req, res)
return this.handleRequest(ctx, fn)//执行的额时候传入ctx的参数
}
}
listen (... args) {
const server = http.createServer(this.callback()) //创建一个服务,并且对服务进行监听
server.listen(... args)
}
}
module.exports = LikeKoa2