一.node创建http服务
./create-node-server.js:
const http = require("http");
const server = http.createServer((req, res) => {
res.end("hello node");
});
server.listen(3001, () => {
console.log("server at 3001");
});
二.使用koa2创建服务
./index.js:
const Koa = require("koa");
const app = new Koa();
app.use(ctx => {
ctx.body = {
name: "Tony"
};
});
app.listen(3000, () => {
console.log("server at 3000");
});
三.手写koa初始源码
const http = require("http");
class Koa {
constructor() {}
use(callback) {
this.callback = callback;
}
listen(...args) {
// 创建服务
const server = http.createServer((req, res) => {
// 执行业务
this.callback(req, res);
});
server.listen(...args);
}
}
module.exports = Koa;
// 测试代码
const Koa = require("./koa");
const app = new Koa();
app.use((req, res) => {
res.end("hello ");
});
app.listen(3000, () => {
console.log("server at 3000");
});
四.引⼊上下⽂context
koa为了能够简化API,引⼊上下⽂context概念,将原始请求对象req和响应对象res封装并分别挂载到request和response上,再挂载到 context上,在context上设置getter和setter,从⽽简化操作。
封装响应对象req的部分内容:
./request.js:
module.exports = {
get url() {
return this.req.url;
},
get method() {
return this.req.method.toLowerCase();
}
};
封装响应对象res的部分内容:
./response.js:
module.exports = {
get body() {
return this._body;
},
set body(val) {
this._body = val;
}
};
将封装好的对象拉到context中:
./context.js
module.exports = {
get url() {
return this.request.url;
},
get method() {
return this.request.method;
},
get body() {
return this.response.body;
},
set body(val) {
this.response.body = val;
}
};
创建上下文时链接context、request、req、response、res。
在创建服务是创建上下文,将上下文传给业务,最后做出响应。
./koa.js:
const http = require("http");
const context = require("./context");
const request = require("./request");
const response = require("./response");
class Koa {
constructor() {}
use(callback) {
this.callback = callback;
}
listen(...args) {
// 创建服务
const server = http.createServer((req, res) => {
// 执行业务
const ctx = this.createContext(req, res);
this.callback(ctx);
// 响应
res.end(ctx.body);
});
server.listen(...args);
}
// 创建上下文方法
createContext(req, res) {
const ctx = Object.create(context);
console.log(ctx);
ctx.request = Object.create(request);
ctx.response = Object.create(response);
ctx.req = ctx.request.req = req;
ctx.res = ctx.response.res = res;
}
}
module.exports = Koa;
五、引入中间件
Koa中间件机制:Koa中间件机制就是函数式组合概念 Compose的概念,将⼀组需要顺序执⾏的 函数复合为⼀个函数,外层函数的参数实际是内层函数的返回值。洋葱圈模型可以形象表示这种机 制。
思路:在koa中使用use时,将传入的中间件放到中间件数组中,
在创建服务时将将多个中间件合并,并执行中间件,最后做出响应。
./koa.js:
const http = require("http");
const context = require("./context");
const request = require("./request");
const response = require("./response");
class Koa {
constructor() {
this.middlewares = [];
}
use(middleware) {
// 将中间件添加到数组中
this.middlewares.push(middleware);
}
listen(...args) {
// 创建服务
const server = http.createServer(async (req, res) => {
// 执行业务
const ctx = this.createContext(req, res);
// 中间件合成
const fn = this.compose(this.middlewares);
// 执行合成函数并传入上下文
await fn(ctx);
// 响应
res.end(ctx.body);
});
server.listen(...args);
}
// 创建上下文方法
createContext(req, res) {
const ctx = Object.create(context);
ctx.request = Object.create(request);
ctx.response = Object.create(response);
ctx.req = ctx.request.req = req;
ctx.res = ctx.response.res = res;
// console.log("ctx1", ctx);
return ctx;
}
// 合成函数
compose(middlewares) {
return function(ctx) {
return dispatch(0);
// 定义dispatch
function dispatch(i) {
let fn = middlewares[i];
// 如果没有fn,则返回空promise
if (!fn) {
return Promise.resolve();
}
// 如果有fn,则返回执行fn的Promise
return Promise.resolve(
// 定义next函数
fn(ctx, function next() {
return dispatch(i + 1);
})
);
}
};
}
}
module.exports = Koa;
测试代码:
const Koa = require("./koa");
const app = new Koa();
const delay = () => new Promise(resolve => setTimeout(() => resolve(), 2000));
app.use(async (ctx, next) => {
ctx.body = "1";
await next();
ctx.body += "5";
});
app.use(async (ctx, next) => {
ctx.body += "2";
await delay();
await next();
ctx.body += "4";
});
app.use(async (ctx, next) => {
ctx.body += "3";
});
app.listen(3000, () => {
console.log("server at 3000");
});
返回:12345