time: 2018.11.1
udpate: 2021-09-02 17:21:18
目录
1 目的
2 koa 基础总结
2.1 koa 对象方法
2.2 上下文 Context
2.3 Request 别名
2.4 Response 别名
2.5 koa response
3 koa 源码解析
已经掌握了前端开发,目前向中间层学习,准备入手 nodejs 做服务端,但是又没有找到合适的学习方式。
看到 thinkjs 和 egg 都是基于 koa 进行扩展的,打算先学习一下 koa,通过 koa 学习 nodejs 的应用,在服务端的位置等。
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
await next();
const rt = ctx.response.get('X-Response-Time');
console.log(`${ctx.method} ${ctx.url} - ${rt}`);
});
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.set('X-Response-Time', `${ms}ms`);
});
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(3000);
const app = new Koa();
最重要的2个 listen
、 use
属于 nodejs http 的语法糖
const http = require('http');
http.createServer(app.callback()).listen(3000);
http.createServer() 的回调函数
给应用程序添加 中间件
设置签名的 Cookie 密钥
访问 ctx
对象的原型,可以为其添加属性,将会在整个应用程序中使用到。
监听应用全局错误
app.on('error', (err, ctx) => {
log.error('server error', err, ctx)
});
在每个中间件中,其参数包含了 ctx
对象
ctx.respond = false
,可以绕过 koa 内置的 response 处理,操作原始的 response 对象ctx.header ctx.headers ctx.method ctx.method= ctx.url ctx.url= ctx.originalUrl ctx.origin ctx.href ctx.path ctx.path= ctx.query ctx.querystring ctx.host ctx.hostname ctx.fresh ctx.stale ctx.socket ctx.protocol ctx.secure ctx.ip ctx.ips ctx.subdomains ctx.is() ctx.accepts() ctx.acceptsEncodings() ctx.acceptsCharsets() ctx.acceptsLanguages() ctx.get()
ctx.body ctx.status ctx.message ctx.length ctx.type ctx.headerSent ctx.redirect() ctx.attachment() ctx.set() ctx.append() ctx.remove() ctx.lastModified ctx.etag=
该 response 对象是 koa 在 node 的 response 对象上的抽象
响应状态
,(默认是 404,不是 node 的200)状态消息
Content-Length
Last-Modified
响应头,同 ctx.response.lastModified注意:response 可以通过 ctx.response 访问到,所以它的属性或方法的别名,可以通过 ctx.body 或 ctx.response.body 访问
response.body 类型与 http 协议的 Content-Type
字段关系
http 基础 这里看 http 基础知识
text/html | text/plain
application/octet-stream
application/octet-stream
application/json
Object 通常为普通对象、数组从源码了解 koa 的整体功能,了解核心 api 实现,比如 koa 对象,实例方法 use, listen,ctx 对象等
问题:
koa 对象核心代码
module.exports = class Application extends Emitter {
constructor (options) {
super()
this.middleware = []
this.context = Object.create(context)
this.request = Object.create(request)
this.response = Object.create(response)
}
listen (...args) {
const server = http.createServer(this.callback())
return server.listen(...args)
}
use (fn) {
this.middleware.push(fn)
return this
}
callback () {
const fn = compose(this.middleware)
const handleRequest = (req, res) => {
const ctx = this.createContext(req, res)
return this.handleRequest(ctx, fn)
}
return handleRequest
}
handleRequest (ctx, fnMiddleware) {
const res = ctx.res
res.statusCode = 404
const onerror = err => ctx.onerror(err)
const handleResponse = () => respond(ctx)
onFinished(res, onerror)
return fnMiddleware(ctx).then(handleResponse).catch(onerror)
}
}
function respond (ctx) {
const res = ctx.res
let body = ctx.body
body = JSON.stringify(body)
res.end(body)
}
归纳总结:
http.createServer
创建服务器,封装了自身的插件规范、request、response,没有多做什么事情app.use(async (ctx, next) => {
await next();
const rt = ctx.response.get('X-Response-Time');
});
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.set('X-Response-Time', `${ms}ms`);
});
app.use(async ctx => {
ctx.body = 'Hello World';
});
前面总结到如下内容
async
函数,内部处理异步逻辑compose
调用所有中间件,compose 返回一个函数来看看 compose 是如何调用函数数组的
function flatten (arr) {
return arr.reduce((acc, next) => acc.concat(Array.isArray(next) ? flatten(next) : next), [])
}
function compose (middleware) {
middleware = flatten(middleware)
return function (context, next) {
// last called middleware #
let index = -1
return dispatch(0)
function dispatch (i) {
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
index = i
let fn = middleware[i]
if (i === middleware.length) fn = next
if (!fn) return Promise.resolve()
try {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
}
归纳总结
dispatch
执行器,也就是 next()
async
异步函数原理每次 http 请求调用的 callback
callback () {
const fn = compose(this.middleware)
const handleRequest = (req, res) => {
const ctx = this.createContext(req, res)
return this.handleRequest(ctx, fn)
}
return handleRequest
}
createContext
createContext (req, res) {
const context = Object.create(this.context)
const request = context.request = Object.create(this.request)
const response = context.response = Object.create(this.response)
context.app = request.app = response.app = this
context.req = request.req = response.req = req
context.res = request.res = response.res = res
request.ctx = response.ctx = context
request.response = response
response.request = request
context.originalUrl = request.originalUrl = req.url
context.state = {}
return context
}
归纳分析: