预备知识
getter/setter 方法包装数据的访问
const obj = {
info : { name : "jason"},
get name(){
return this.info.name;
},
set name(val){
console.log("开始设置新内容",val)
this.info.name = val
}
}
console.log(obj.name)
obj.name = "xxx"
console.log(obj.name)
koa 框架实现
整体代码结构
myKoa源码实现
//myKoa.js //主代码
//context.js //记录上下文 封装了 req 和 res
//request.js //重写http原来的request对象
//response.js //重写http原来的response对象
//app.js //程序入口
//myKoa.js
const http = require("http")
const context = require('./context')
const request = require('./request')
const response = require('./response')
class myKoa {
constructor(){
this.middlewares = []
}
listen(...args){
const server = http.createServer(async (req, res) => {
// 创建上下文
const ctx = this.createCTX(req, res)
const fn = this.compose(this.middlewares)
await fn(ctx)
// this.callback(req,res)
// this.callback(ctx)
res.end(ctx.body)
})
server.listen(...args)
}
use(middleware){
this.middlewares.push(middleware)
}
createCTX(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
return ctx
}
compose(middlewares) {
return function (ctx) {
return dispatch(0)
function dispatch(i) {
let fn = middlewares[i]
if (!fn) {
return Promise.resolve()
}
return Promise.resolve(
fn(ctx, function next() {
return dispatch(i + 1)
})
)
}
}
}
}
module.exports = myKoa
//context.js
module.exports = {
get url() {
return this.request.url
},
get body() {
return this.response.body
},
set body(val){
this.response.body = val
},
get method() {
return this.request.method
}
}
//request.js
module.exports ={
get url(){
return this.req.url
},
get method(){
return this.req.method.toLowerCase()
}
}
//response.js
module.exports = {
get body(){
return this._body
},
set body(val){
this._body = val
}
}
//程序测试入口 app.js
const myKoa = require('./myKoa')
const app = new myKoa()
const delay = () => Promise.resolve(resolve => setTimeout(() => resolve(), 2000));
app.use(async (ctx, next) => {
ctx.body = "1";
setTimeout(() => {
ctx.body += "2";
}, 2000);
await next();
ctx.body += "3";
});
app.use(async (ctx, next) => {
ctx.body += "4";
await delay();
await next();
ctx.body += "5";
});
app.use(async (ctx, next) => {
ctx.body += "6";
});
app.listen(3000)
koa-router路由的实现
- 使用策略模式/决策模式, 原理:注册表匹配读取方式
- 保存全局的数组列表对象stack对象,
- 每次注册记录对象route:请求方式,地址,对应方法
- 每次响应匹配对应的 请求方式,地址,执行对应方法
//router.js
class Router {
constructor() {
this.stack = [];
}
register(path, methods, middleware) {
let route = {path, methods, middleware}
this.stack.push(route);
}
// 现在只支持get和post,其他的同理
get(path,middleware){
this.register(path, 'get', middleware);
}
post(path,middleware){
this.register(path, 'post', middleware);
}
routes() {
let stock = this.stack;
return async function(ctx, next) {
let currentPath = ctx.url;
let route;
for (let i = 0; i < stock.length; i++) {
let item = stock[i];
if (currentPath === item.path && item.methods.indexOf(ctx.method) >= 0) {
// 判断path和method
route = item.middleware;
break;
}
}
if (typeof route === 'function') {
route(ctx, next);
return;
}
await next();
};
}
}
module.exports = Router;
//测试代码app.js
const myKoa = require('./myKoa')
const app = new myKoa()
const Router = require('./router')
const router = new Router()
router.get('/index', async ctx => { ctx.body = 'index page'; });
router.get('/post', async ctx => { ctx.body = 'post page'; });
router.get('/list', async ctx => { ctx.body = 'list page'; });
router.post('/index', async ctx => { ctx.body = 'post page'; });
app.use(router.routes());
app.listen(3000)
koa-static静态资源响应实现
文件结构
// static.js
const fs = require("fs");
const path = require("path");
module.exports = (dirPath = "./public") => {
return async (ctx, next) => {
if (ctx.url.indexOf("/public") === 0) {
// public开头 读取文件
const url = path.resolve(__dirname, dirPath);
const fileBaseName = path.basename(url);
const filepath = url + ctx.url.replace("/public", "");
console.log(filepath);
// console.log(ctx.url,url, filepath, fileBaseName)
try {
stats = fs.statSync(filepath);
if (stats.isDirectory()) {
const dir = fs.readdirSync(filepath);
// const
const ret = ['<div style="padding-left:20px">'];
dir.forEach(filename => {
console.log(filename);
// 简单认为不带小数点的格式,就是文件夹,实际应该用statSync
if (filename.indexOf(".") > -1) {
ret.push(
`<p><a style="color:black" href="${
ctx.url
}/${filename}">${filename}</a></p>`
);
} else {
// 文件
ret.push(
`<p><a href="${ctx.url}/${filename}">${filename}</a></p>`
);
}
});
ret.push("</div>");
ctx.body = ret.join("");
} else {
console.log("文件");
const content = fs.readFileSync(filepath);
ctx.body = content;
}
} catch (e) {
// 报错了 文件不存在
ctx.body = "404, not found";
}
} else {
// 否则不是静态资源,直接去下一个中间件
await next();
}
};
};
//测试代码app.js
const myKoa = require('./myKoa')
const app = new myKoa()
const static = require('./static')
app.use(static(__dirname + '/public'));
app.listen(3000)
访问地址