Demo地址
Init 开发环境
1, 初始化很简单,直接使用yarn init就可以。
2, 创建源码目录src
搭建基本的web服务
1, 这里很简单,就是使用把http.creatServe包装一层,代码很简单 1.1 src/app.js
const http = require("http");
class MyKoa {
constructor(props) {
this.midderWare = [];
}
use(fn) {
this.midderWare.push(fn);
}
listen(...args) {
const server = http.createServer((req, res) => {
this.midderWare.forEach(function(fn) {
return fn(req, res);
});
});
server.listen(...args);
}
}
module.exports = MyKoa;
1.2 src/index.js
const MyKoa = require("./app.js");
const app = new MyKoa();
app.use((req, res) => {
console.log("11111111");
});
app.use((req, res) => {
console.log("22222222");
res.end("hello world");
});
// 监听端口号
app.listen(3011, err => {
console.log(err);
if (!err) {
console.log("server is starting");
} else {
console.log(err);
}
});
2, vscode中添加lanch.json, 方便使用vscode进行调试。
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "启动程序",
"runtimeExecutable": "/.nvm/versions/node/v10.15.3/bin/node",
"program": "/work/self_koa/src/index.js"
}
]
}
封装原生的req,res和context
koa里边的req和res都不是原生的req和res,它是在原生的req和res上,为了使用方便,而进行重新进行封装了一层,让用户用起来更加方便。
封装req
其实封装很简单,顾名思义就是把req包装一下,让它有更多的特性,更加的方便使用。
1, 新建src/request.js
let url = require("url");
module.exports = {
get query() {
return url.parse(this.req.url, true).query;
}
};
代码很简单,没什么好说的。
2, 测试requeset.js
上面的requeset.js, 可以看到它是使用了this.req.url, 所以需要改变一下this,让this对象能够有req对象,不然在request里边没法使用。
使用this.request.req = req来达到此目的。
const http = require("http");
const request = require("./request.js");
class MyKoa {
constructor(props) {
this.midderWare = [];
this.request = request;
}
use(fn) {
this.midderWare.push(fn);
}
listen(...args) {
const server = http.createServer((req, res) => {
this.request.req = req;
this.midderWare.forEach(fn => {
return fn(this.request.query, res);
});
});
server.listen(...args);
}
}
module.exports = MyKoa;
封装res
res的封装和封装req基本上差不多,可以根据自己的添加需要的方法和属性,
1, 新建src/response.js
module.exports = {
get status() {
return 300;
},
/**
* 设置返回给客户端的stausCode
*
* @param {number} statusCode 状态码
*/
set status(statusCode) {
if (typeof statusCode !== "number") {
throw new Error("statusCode must be a number!");
}
this.res.statusCode = statusCode;
}
};
2, 测试response.js
修改app.js,和封装req一样,也需要把res传到对象里边,也就是用this.response.res=res
const http = require("http");
const request = require("./request.js");
const response = require("./response.js");
class MyKoa {
constructor(props) {
this.midderWare = [];
this.request = request;
this.response = response;
}
use(fn) {
this.midderWare.push(fn);
}
listen(...args) {
const server = http.createServer((req, res) => {
this.request.req = req;
this.response.res = res;
this.midderWare.forEach(fn => {
return fn(this.request.query, this.response);
});
});
server.listen(...args);
}
}
module.exports = MyKoa;
修改src/index.js
const MyKoa = require("./app.js");
const app = new MyKoa();
app.use((req, res) => {
console.log("11111111", res.status);
});
app.use((req, res) => {
console.log("22222222", req);
res.res.end("hello world");
});
// 监听端口号
app.listen(3011, err => {
console.log(err);
if (!err) {
console.log("server is starting");
} else {
console.log(err);
}
});
测试结果
添加中间件
中间件其实也很简单,和redux的中间件有点类似,不过redux的中间件是通过来重写store.dispatch来实现,而koa的中间件也是通过控制中间件的顺序来实现,不过koa是通过重写next来实现的。 假设有三个中间件分别如下
async (req, res, next) => {
console.log("1");
await next(); // 调用下一个middleware
console.log("5");
}
async (req, res, next) => {
console.log("2");
await next(); // 调用下一个middleware
console.log("4");
}
async (req, res, next) => {
console.log("3");
}
控制中间的顺序compose函数
代码很简单,就是不断的重写next,基本上和redux的思想差不多
compose() {
return async (req, res) => {
let next = async () => {
return Promise.resolve();
};
const creatNext = (current, next) => {
return async () => {
await current(req, res, next);
};
};
for (let i = this.midderWare.length - 1; i >= 0; i--) {
next = creatNext(this.midderWare[i], next);
}
await next();
};
}
1, 修改src/app.js
const http = require("http");
const request = require("./request.js");
const response = require("./response.js");
class MyKoa {
constructor(props) {
this.midderWare = [];
this.request = request;
this.response = response;
}
use(fn) {
this.midderWare.push(fn);
}
compose() {
return async (req, res) => {
let next = async () => {
return Promise.resolve();
};
const creatNext = (current, next) => {
return async () => {
await current(req, res, next);
};
};
for (let i = this.midderWare.length - 1; i >= 0; i--) {
next = creatNext(this.midderWare[i], next);
}
await next();
};
}
listen(...args) {
const server = http.createServer((req, res) => {
this.request.req = req;
this.response.res = res;
let fn = this.compose();
return fn(req, res);
});
server.listen(...args);
}
}
module.exports = MyKoa;
2, 修改src/index.js
const MyKoa = require("./app.js");
const app = new MyKoa();
app.use(async (req, res, next) => {
console.log("1");
await next(); // 调用下一个middleware
console.log("5");
});
app.use(async (req, res, next) => {
console.log("2");
await next(); // 调用下一个middleware
console.log("4");
});
app.use(async (req, res, next) => {
console.log("3");
// res.end("hello");
// res.res.end("hello world");
});
// 监听端口号
app.listen(3011, err => {
if (!err) {
console.log("server is starting");
} else {
console.log(err);
}
});