基于Node.js平台的下一代Web开发框架数组
Koa的基本用法比较简单,并且没有捆绑任何中间件bash
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(3000);
复制代码
由Koa的用法能够看出Koa自己是一个类,有use和listen方法,废话很少说,上代码。 Koa基本代码结构,核心代码文件 application.js app
测试文件test.js框架
let Koa = require('./koa/application')
let app = new Koa()
app.use((req,res)=>{
res.end('jeffywin')
})
app.listen(3000)
复制代码
application.jskoa
/**
* 核心方法 use listen handleRequest
*/
let http = require('http')
class Koa {
constructor(){
this.callbackFn;
}
//test.js中的use回调传给cb,而后存到this.callbackFn中
use(cb) {
this.callbackFn = cb;
}
//1.test.js中use方法中要如何能拿到req,res,首先handleRequest接收listen中的req,res
//2.接着req,res传给this.callbackFn,也就是上面use方法中赋值的this.callbackFn
handleRequest(req,res){
console.log(req,res)
this.callbackFn(req,res)
}
//listen方法中接收test.js listen方法传递过来的参数,接着将req,res传给方法handleRequest
listen(...args) {
let server = http.createServer(this.handleRequest.bind(this))
server.listen(...args)
}
}
module.exports = Koa;
复制代码
更新test.js中use方法,实现下面4个ctx中的属性异步
let Koa = require('./koa/application')
let app = new Koa()
//ctx是对原生req,res的封装
app.use((ctx,next)=>{
console.log(ctx.req.path) //ctx.req = req
console.log(ctx.request.req.path) //ctx.request.req = req
console.log(ctx.request.path) //ctx.request 是Koa本身封装的
console.log(ctx.path) //ctx代理ctx.request ctx.path === ctx.request.path
ctx.body = 'jeffywin'
})
app.listen(3000)
复制代码
application.jsasync
/**
* 核心方法 use listen handleRequest
*/
let http = require('http')
let Stream = require('stream');
let context = require('./context')
let response = require('./response')
let request = require('./request')
class Koa {
constructor(){
this.callbackFn;
this.context = context
this.response = response
this.request = request
}
//test.js中的use回调传给cb,而后存到this.callbackFn中
use(cb) {
this.callbackFn = cb;
}
createContext(req,res) {
//ctx能够拿到context的属性,但又不修改context,经过Object.create()至关于建立匿名函数
let ctx = Object.create(this.context)
ctx.request = Object.create(this.request)
ctx.response = Object.create(this.response)
ctx.req = ctx.request.req = req//ctx本身添加属性request
ctx.res = ctx.response.res = res//ctx本身添加属性response
return ctx
}
//1.use方法中要能拿到req,res,首先handleRequest接收listen中的req,req
//2.接着req,res传给this.createContext获得ctx,也就是上面use方法中赋值的this.callbackFn
handleRequest(req,res){
res.statusCode = 404
//建立上下文
let ctx = this.createContext(req,res)
this.callbackFn(ctx)
let body = ctx.body
if (body instanceof Stream){
body.pipe(res);
}else if(Buffer.isBuffer(body) || typeof body === 'string'){
res.end(body);
}else if(typeof body === 'object' ){
res.end(JSON.stringify(body));
}else{
res.end(body);
}
}
//listen方法中接收test.js listen方法传递过来的参数,接着将req,res传给方法handleRequest
listen(...args) {
let server = http.createServer(this.handleRequest.bind(this))
server.listen(...args)
}
}
module.exports = Koa;
复制代码
request.js函数
//ctx.request.url = ctx.url
let url = require('url')
let request = {
//get方法至关于Object.defineProperty()的get方法的简写,只要调ctx.request的url方法
//就会返回this.req.url,好比ctx.request.url,此时this == ctx.request
//当调用ctx.request.url 就至关于调用 ctx.request.req.url,然后者在application.js中能够拿到
get url(){
return this.req.url
},
get path() {
return url.parse(this.req.url).pathname
}
}
module.exports = request;
复制代码
response.js测试
let response = {
set body(val) {
this.res.statusCode = 200
this._body = val
},
get body(){
return this._body
}
}
module.exports = response;
复制代码
context.jsui
//怎么直接ctx.url;方法,用ctx代理ctx.request,用原生方法__defineGetter__进行封装
let proto = {}
function defineGetter(property,name) {
proto.__defineGetter__(name,function(){
return this[property][name]
})
}
function defineSetter(property,name) {
proto.__defineSetter__(name,function(val){
this[property][name] = val
})
}
//调用ctx.url 至关于调用this[property][name],也就是this.request.url
defineGetter('request','url')
defineGetter('request','path')//ctx.path = ctx.request.path
defineGetter('response','body')//ctx.body = ctx.response.body
defineSetter('response','body')
module.exports = proto;
复制代码
test.js
let Koa = require('./koa/application')
let app = new Koa()
app.use((ctx,next)=>{
console.log(1)
next()
console.log(2)
})
app.use((ctx,next)=>{
console.log(3)
next()
console.log(4)
})
app.use((ctx,next)=>{
console.log(5)
next()
console.log(6)
})
app.listen(3000)
复制代码
next()方法至关于下一个执行函数
// 模拟Koa中间件实现原理
let fs = require('fs');
function app() = {}
app.middlewares = []//声明一个数组
app.use = function(cb){
app.middlewares.push(cb)
}
app.use(async (ctx, next) => {
console.log(1);
await next();
console.log(2);
});
app.use(async (ctx, next) => {
console.log(3);
await next();
console.log(4);
});
app.use(async (ctx, next) => {
console.log(5);
await next();
console.log(6);
});
function dispatch(index){
if(index === add.middlewares.length) return
let middleware = app.middlewares[index]
middle({},()=>dispatch(index+1))//递归调用下一个中间件,next()
}
dispatch(0)//让第一个中间件执行
复制代码
回到test.js中
let Koa = require('./koa/application')
let app = new Koa()
//Koa可使用 async await
//第一个use中使用了await next(),此时next是第二个use中的 async函数,而且因为是await,因此第二个use中返回的是Promise,须要等待执行完才能够执行下面代码,同理,第二个next要等待第三个函数执行完,这些中间件会造成一个Promise链
let log = () => {
return new Promise((resolve,reject) =>{
setTimeout(()=>{
console.log('Ok')
resolve()
},1000)
})
}
app.use(async (ctx, next) => {
console.log(1);
await next();
console.log(2);
});
app.use(async (ctx, next) => {
console.log(3);
await log();
await next();
console.log(4);
});
app.use(async (ctx, next) => {
console.log(5);
await next();
console.log(6);
ctx.body = 'jeffywin'
});
app.on('error', err => {
console.log(err);
})
app.listen(3000);
// 1 3 Ok 5 6 4 2
复制代码
最终版application.js
let http = require('http')
let Stream = require('stream');
let EventEmitter = require('events');
let context = require('./context')
let response = require('./response')
let request = require('./request')
class Koa extends EventEmitter{
constructor(){
super();
this.middlewares = []
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
}
use(cb) {
this.middlewares.push(cb)
}
createContext(req,res) {
//ctx能够拿到context的属性,但又不修改context,经过Object.create()至关于建立匿名函数
let ctx = Object.create(this.context)
ctx.request = Object.create(this.request)
ctx.response = Object.create(this.response)
ctx.req = ctx.request.req = req//ctx本身添加属性request
ctx.res = ctx.response.res = res//ctx本身添加属性response
return ctx
}
//核心方法 compose
compose(ctx,middlewares){
function dispatch(index) {
if(index === middlewares.length) return Promise.resolve();
let fn = middlewares[index];
return Promise.resolve(fn(ctx,()=>dispatch(index+1)));
}
return dispatch(0);
}
//1.use方法中要能拿到req,res,首先handleRequest接收listen中的req,res
//2.接着req,res传给this.createContext获得ctx
handleRequest(req,res){
res.statusCode = 404
//建立上下文
let ctx = this.createContext(req,res)
let composeMiddleware = this.compose(ctx, this.middlewares)//返回的确定是Promise
//当Promise都执行完后,执行成功回调
composeMiddleware.then(()=>{
let body = ctx.body
if (body instanceof Stream){
body.pipe(res);
}else if(Buffer.isBuffer(body) || typeof body === 'string'){
res.end(body);
}else if(typeof body === 'object' ){
res.end(JSON.stringify(body));
}else{
res.end(body);
}
}).catch(err => {
this.emit('error', err);
});
}
//listen方法中接收test.js listen方法传递过来的参数,接着将req,res传给方法handleRequest
listen(...args) {
let server = http.createServer(this.handleRequest.bind(this))
server.listen(...args)
}
}
module.exports = Koa;
复制代码