前言
- 找到koa源码的package.json文件,找到”main”: “lib/application.js”,此时我们调用的Koa的类就是在application.js中的
- application主要做的事情就是实例化应用,
- context主要做的事情就是实例上下文,
- request.js由原生request事件的http.IncomingMessage类过滤而来;
- response.js对应ctx.response,由原生request事件的http.ServerResponse类过滤而来。
处理中间件
我们调用中间件的时候传入的callback,callback循环被组合的中间件,递归调用中间件 ```javascript /**
- Shorthand for: *
- http.createServer(app.callback()).listen(…) *
- @param {Mixed} …
- @return {Server}
- @api public */
listen(…args) { debug(‘listen’); const server = http.createServer(this.callback()); return server.listen(…args); }
callback() { const fn = compose(this.middleware);
if (!this.listenerCount('error')) this.on('error', this.onerror);
const handleRequest = (req, res) => {
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};
return handleRequest;
}
<a name="f7n8H"></a>
#### context->ctx别名处理
- koa内部使用的是proto.__defineSetter__和proto.__defineGetter__ [__defineGetter__(mdn已废弃)](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/__defineGetter__)
- 也可以使用Object.defineProperty
```javascript
const context = {
// get method() {
// return this.request.method
// },
// get url(){
// return this.request.url
// }
}
defineProperty('request', 'method')
defineProperty('request', 'url')
defineProperty('request', 'body')
function defineProperty(target, name) {
// context.__defineGetter__(name, function () {
// return this[target][name]
// })
Object.defineProperty(context, name, {
get() {
return this[target][name]
},
set(value) {
this[target][name] = value
}
})
}
module.exports = context
简单的实现
application.js
洋葱结构,当初为什么要这样设计呢?因为是为了解决复杂应用中频繁的回调而设计的级联代码,它并不会把控制权完全交给一个中间件的代码,而是碰到next就会去下一个中间件,等下面所有中间件执行完成后,就会再回来执行中间件未完成的代码。
为什么要返回promise呢
所以Promise.resolve的作用, 在普通函数中依旧能使用next().then()的形式,保证中间件的正确运行
- 如果所有的中间件都为普通函数并且没有异步,洋葱模型的实现与调用栈有关,
由于async,await的特性,所有的中间件都使用async/await 也可以保持异步的情况下保持洋葱模型,
此时都可以使用compose-sync 达到与compose-async同样的效果
但当要在普通函数中实现异步保持洋葱模型,则需要compose-async 返回promise, 以达到与async/await 同样的效果。
const http = require('http')
const context = require('./context')
const request = require('./request')
const response = require('./response')
class Application {
constructor() {
// 保存用户添加的中间件函数
this.middleware = []
// 为了方便实用,直接添加到application实例上
// 如果有多个实例共享的时候,数据容易受到污染
this.context = Object.create(context)
this.request = Object.create(request)
this.response = Object.create(response)
}
listen(...args) {
// 把相关逻辑抽离出去
const server = http.createServer(this.callback())
server.listen(...args)
}
// 当调用use的时候,调用的处理函数(中间件),往middleware中push中间件
use(fn) {
this.middleware.push(fn)
}
// 异步递归遍历调用中间件处理函数 next
compose(middleware) {
return function (context) {
const dispatch = index => {
// next就是dispatch,处理index边界问题
// 整个返回的都是promise,所以在超出边界的时候也要返回promise.resolve成功的状态
if (index >= middleware.length) return Promise.resolve()
const fn = middleware[index]
// 把fn强制的包装到一个promise里面
return Promise.resolve(
// 上下文对象
fn(context, () => dispatch(index + 1)) // 这是next函数
)
}
// 返回第一个中间件处理函数
// dispatch不是一数组。是一个函数,调用dispatch
return dispatch(0)
}
}
// 创建上下文对象的函数
// 构造上文对象
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;
console.log(req);
context.originalUrl = request.originalUrl = req.url;
context.state = {};
return context;
}
callback() {
// compose进行递归遍历,把当前存储中间件函数的数组存储进来
// 扩展性更好,middleware可以通过参数传进来
const fnMiddleware = this.compose(this.middleware)
const handleRequest = (req, res) => {
// 最终对应的结果
// 每个请求都会创建一个独立的context对象,他们之间不会互相污染
const context = this.createContext()
fnMiddleware(context).then(() => {
// console.log('end');
res.end(context.body)
// res.end('My Koa')
}).catch(err => {
res.end(err.message)
})
}
return handleRequest
}
}
module.exports = Application
context
const context = {
// get method() {
// return this.request.method
// },
// get url(){
// return this.request.url
// }
}
defineProperty('request', 'method')
defineProperty('request', 'url')
defineProperty('request', 'body')
function defineProperty(target, name) {
// context.__defineGetter__(name, function () {
// return this[target][name]
// })
Object.defineProperty(context, name, {
get() {
return this[target][name]
},
set(value) {
this[target][name] = value
}
})
}
module.exports = context
response
const response = {
set status(val) {
this.res.statusCode = val
},
_body: '', // 真正用来存储数据的
get body() {
return this._body
},
set body(value) {
this._body = value
}
}
module.exports = response
request
const url = require('url')
const request = {
// 对象属性访问器
get method() {
console.log(this);
return this.req.method
},
get header() {
return this.req.headers
},
get url() {
return this.req.url
},
get path() {
return url.parse(this.req.url).pathname
},
get query() {
return url.parse(this.req.url, true).query
}
}
module.exports = request