коа кадр
Многие проекты сейчас реализуются на основе koa framework, в основном из-за того, что koa маленький и легкий, и использует плагины-расширения.Различные плагины можно выбирать в соответствии с требуемыми функциями, что более удобно и быстро в разработке. Поэтому очень, очень, очень необходимо понять принцип реализации коа.
коа серия статей
- Будет использоваться и написана структура koa (инфраструктура koa)
- Будет использоваться и написана структура koa — (koa-router)
- Будет использоваться и написана структура koa — (koa-view, koa-static)
- Фреймворк koa можно использовать и писать — (koa-bodyparser, koa-better-body)
Анализ употребления слова коа
const Koa = require('koa');
let app = new Koa();//Koa是一个类,通过new生成一个实例
//koa的原型上有use方法,来注册中间件
app.use((ctx,next)=>{
//koa拥有ctx属性,上面挂载了很多属性
console.log(ctx.req.path);
console.log(ctx.request.req.path);
console.log(ctx.request.path);
console.log(ctx.path);
next();//洋葱模型,中间件组合
})
app.listen(3000);//Koa的原型上拥有监听listen
Комбинация луковой модели и промежуточного программного обеспечения
луковая модель
const Koa = require('koa');
const app = new Koa();
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)
awit next();
console.log(6)
})
//打印结果:1 3 5 6 4 2
Состав промежуточного программного обеспечения
Реализация луковой модели koa на самом деле состоит в том, чтобы сохранить функцию в очереди промежуточного ПО через использование, а затем отправить промежуточное ПО через диспетчеризацию функции.
- Промежуточное ПО композиции отправки:
let app = {
middlewares:[]; //缓存队列
use(fn){ //注册中间件
this.middlewares.push(fn);
}
}
app.use(next=>{
console.log(1)
next();
console.log(2)
});
app.use(next => {
console.log(3)
next();
console.log(4)
})
app.use(next => {
console.log(5)
next();
console.log(6)
})
dispatch(0)
function dispatch(index){ //派发执行中间件
if(index===app.middlewares.length) retrun ;
let middleware = app.middlewares[index];
middleware(()=>{
dispatch(index+1);
})
}
- Промежуточное ПО композиции Array.prototype.reduceRight:
let app = {
middlewares:[];//缓存队列
use(fn){//注册中间件
this.middlewares.push(fn);
}
}
app.use(next=>{ //fn1(next) next => fn2
console.log(1)
next();
console.log(2)
});
app.use(next => { //fn2(next) next => fn3
console.log(3)
next();
console.log(4)
})
app.use(next => { //fn3(next) next => null;
console.log(5)
next();
console.log(6)
})
let fn= compose(app.middlewares)
function conpose(middle){
return middles.reduceRight((a,b)=>{ //收敛成一个函数
return function(){
b(a);
}
},()=>{});
}
fn();
//fn3(next) next:() => {};
//fn2(next) next:() => fn3(()=>{})
//fn1(next) next:() => fn2(()=>fn3(()=>{}))
- Промежуточное ПО композиции Array.prototype.reduce:
let app = {
middlewares:[];//缓存队列
use(fn){//注册中间件
this.middlewares.push(fn);
}
}
app.use(next=>{ //fn1(next) next => fn2
console.log(1)
next();
console.log(2)
});
app.use(next => { //fn2(next) next => fn3
console.log(3)
next();
console.log(4)
})
app.use(next => { //fn3(next) next => null;
console.log(5)
next();
console.log(6)
})
let fn= compose(app.middlewares)
function conpose(middle){
return middles.reduce((a,b)=>{ //收敛成一个函数
return (arg)=>{
a(()=>{b(arg)})
}
});
}
fn(()=>{});
компоненты коа
коа в основном состоит из четырех частей:
- приложение: основная логика koa, включая промежуточную обработку
- контекст: инкапсуляция ctx в koa
- запрос: инкапсуляция объекта запроса koa
- ответ: инкапсуляция объекта ответа koa
Реализация коа
прототипное наследование
let proto = {
//类似Object.defineProperty(request,'url'){get(){...}}
get height(){
// this => example
return this.options.height
},
name: 'proto'
}
let example = Object.create(proto)
console.log(proto) //{name:'proto'}
console.log(example) //{}
example.name = 'example'
example.options = {height:170,weight:50}
console.log(proto) //{name:'proto'}
console.log(example) //{name:'example',options:{height:170,weight:50}}
console.log(example.height) //170
/**************************************************************/
let proto = {
name: 'proto'
}
let sub = proto
console.log(proto) //{name:'proto'}
console.log(sub) //{name:'proto'}
sub.name = 'sub'
console.log(proto) //{name:'sub'}
console.log(sub) //{name:'sub'}
инкапсуляция запроса
Развернуть URL, путь и другие атрибуты по запросу
//request.js
let request = {
get url(){
// this => ctx.request
// ctx.request.req.url
this.req.url;
}
get path(){
let url = require('url');
return url.parse(this.req.url).pathname;
}
}
module.exports = request;
пакет ответов
Расширение атрибутов, таких как тело, по запросу
//response.js
let response = {
get body(){
return this._body;
}
set body(val){ //设置内置的_body来存储
this._body = val
}
}
module.exports = response;
ctx-пакет
Атрибут ctx представляет некоторые атрибуты ctx.request и ctx.response, так что ctx.xx может получить доступ к ctx.request.xx или ctx.response.xx.
//context.js
let proto = {};
function defineGetter(property,key){
proto.__defineGetter(key,function(){
return this[property][key];
})
}
function defineSetter(property,key){
proto.__defineSetter(key,function(val){
this[property][key] = val;
})
}
defineGetter('request','url'); //ctx代理了ctx.request.url的get
defineGetter('request','path'); //ctx代理了ctx.request.path的get
defineGetter('response','body'); //ctx代理了ctx.response.body的get
defineSetter('response','body'); //ctx代理了ctx.response.body的set
//返回一个对象作为ctx的原型__proto__
module.exports = proto;
Инициализация класса Коа
- Koa — это класс с промежуточным ПО, ctx, запросом, ответом
- Koa.prototype использует промежуточное ПО для регистрации
- Koa.prototype прослушивает сетевые запросы, а его внутренняя часть является инкапсуляцией http-модуля.
- handleRquest в Koa обрабатывает контекст ctx и промежуточное ПО
//application.js
const http = require('http');
let context = require('./context');
let request = require('./request');
let response = require('./response');
class Koa {
constructor(){
this.middlewares = [];
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
}
use(fn){
this.middlewares.push(fn) ;
}
//挂载封装处理ctx
createContext(req,res){
// ctx.request、ctx.response属于koa的对象
// koa插件会在ctx.request上扩展其他属性
let ctx = Object.create(this.context);
ctx.request = Object.create(this.request);
ctx.response = Object.create(this.response);
// http模块原生属性req
ctx.request.req = ctx.req = req;
ctx.response.res = ctx.res = res;
return ctx;
}
//组合中间件
compose(ctx,middles){
function dispatch(index){
if(index === middle.length) return;
let middle = middles[index];
middle(ctx,()=>dispatch(index+1));
}
dispatch(0);
}
//网络请求监听回调
handleRequest(req,res){
// 每次接收到请求会创建一个新的上下文
let ctx = createContext(req,res);
this.compose(ctx,this.middlewares);
}
//监听网络请求
listen(...args){
let server = http.createServer(this.handleRquest);
server.listen(...args)
}
}
module.exports = Koa
Обработка асинхронности и ошибок
Вышеупомянутые функции основаны на синхронных функциях, но большинство из них являются асинхронными функциями в узле, поэтому функции обработки промежуточного программного обеспечения должны быть совместимы с асинхронными функциями. Поскольку async+awit равно генератору+co (koa1.0), а реализация автоматизации генератора в co основана на промисе, то здесь нужно сделать обещание функции. Если вы не понимаете промис, генератор, асинхронность, вы можете прочитать другую статьюПринцип обещания так прост
//application.js
const http = require('http');
let context = require('./context');
let request = require('./request');
let response = require('./response');
let Stream = require('stream');
let EventEmitter = require('events');
class Koa extends EventEmitter { //继承EE,处理错误
constructor(){
this.middlewares = [];
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
}
use(fn){
this.middlewares.push(fn) ;
}
createContext(req,res){
let ctx = this.context;
ctx.request = this.request;
ctx.response = this.response;
ctx.req=ctx.request.req =req;
ctx.res=ctx.response.res=res;
return ctx;
}
compose(ctx,middles){
function dispatch(index){
//没有注册中间件,返回一个promise
if(index === middle.length) return Promise.resolve();
let middle = middles[index];
// Promise化,next一定为promise
return Promise.resolve(middle(ctx,()=>dispatch(index+1)));
}
return dispatch(0);
}
handleRequest(req,res){
res.statusCode = 404;
let ctx = createContext(req,res);
//所有的中间件执行时候,可以执行内置逻辑,处理错误等
let p = this.compose(ctx,this.middlewares);
p.then(()=>{
//统一处理res.body的不同情况
let body = ctx.body;
if (Buffer.isBuffer(body) || typeof body === 'string'){
res.setHeader('Content-Type','text/plain;charset=utf8')
res.end(body);
} else if (body instanceof Stream){
body.pipe(res);
}else if(typeof body == 'object'){
res.setHeader('Content-Type','application/json;charset=utf8')
res.end(JSON.stringify(body));
}else{
res.end('Not Found');
}
}).catch(e=>{ //处理错误
this.emit('error',e);
res.statusCode = 500;
//_http_server可以根据状态码找到对应的类型字段
res.end(require('_http_server').STATUS_CODES[res.statusCode]);
})
}
listen(...args){
let server = http.createServer(this.handleRquest);
server.listen(...args)
}
}
module.exports = Koa
Эпилог
Была представлена базовая структура koa.В дополнение к базовой структуре koa также имеет два важных компонента, маршрутизацию и промежуточное ПО.В следующей статье давайте рассмотрим маршрутизацию koa!