Koa

378 阅读2分钟

1.koa/express区别

  1. 更轻量,无内置static,需要依赖koa-static

  2. koa强依赖router——没有get、post,use也不能指定地址

koa-route
    路由参数        直接给中间件函数加参数——ctx, next
    server.use(route.get(xxx));
	ctx.req/res             原生req、res对象
   	ctx.request/response    koa封装的req、res
   	
koa-router(项目)
    路由参数        ctx.params
    let r1=router();
    server.use(r1.routes());
    r1.get(xxx);

2.中间件

server.use(async (ctx,next)=>{ 
    console.log("a")
	await next(); //next()中存在异步await才有有效的,否则也视为同步代码
    console.log("bb")
})

1.my-koa-static

//my-static
const fs=require('fs');
const assert=require('assert');
module.exports=function (root){
  assert(root, 'argument 1:root is required');
  assert(typeof root=='string', 'root must be a string');
  return async (ctx)=>{
    ctx.response.body=await new Promise((resolve, reject)=>{
      fs.readFile(`${root}${ctx.request.path}`, (err, data)=>{
        if(err){
          reject(err);
        }else{
          resolve(data.toString());
        }
      });
    });
  };
};
//usage server.use(async ()=>{}) 中间只需要返回一个async function即可
server.use(myStatic('www'));

2.koa-router

const koa=require('koa');
const router=require('koa-router');
let server=new koa();
server.listen(8080);
//1.单个路由
let r1=new router();
server.use(r1.routes());
r1.get('/a/:id/:page', async(ctx, next)=>{
    console.log(ctx.params);
    ctx.response.body='abc';
});
server.use(static('www'));
//路由分模块
let mainRouter=new router();
server.use(mainRouter.routes());
mainRouter.use('/user', require('./routers/user'));
mainRouter.use('/user', require('./routers/xxx'));
//user.js --- must return new Router().routes();
//router.use([path], middleware),middleware require function type
let r1=new router();
r1.get('/a', async(ctx)=>{
    ctx.response.body='aaaa';
});
module.exports=r1.routes();

3.koa-better-body / koa-convert

//koa-convert : convert legacy ( 0.x & 1.x ) generator middleware to modern promise middleware ( 2.x ).
//convert(function *(){})   ->    Promise
const body=require('koa-better-body');
const convert=require('koa-convert');
let server=new koa();
server.listen(8080);
server.use(convert(body({
  uploadDir: './upload/',
  keepExtensions: true
})));
server.use(async ctx=>{
  console.log('body: ', ctx.request.body);        //buffer
  console.log('files: ', ctx.request.files);      //文件
  console.log('fields: ', ctx.request.fields);    //数据(包括文件)
});

4.cookie / koa-session

const koa=require('koa');
const session=require('koa-session');
let server=new koa();
server.listen(8080);
server.keys=['asdfasfasfasdf', 'fghdfg45656', 'fdhr67r67utyj'];
server.use(session({
  maxAge: 20*60*1000
}, server));
server.use(async ctx=>{
  if(!ctx.session['n']){
    ctx.session['n']=1;
  }else{
    ctx.session['n']++;
  }
  console.log(ctx.session);
  ctx.response.body=`你是第${ctx.session['n']}次来访`;
});

5.koa-formidable/koa2-cors

3.koa原理

//使用场景
const koa=require('my-koa');
let server=new koa();
server.listen(8080);
server.use(async (ctx, next)=>{
  console.log('a');
  await next(); //next中可能还有异步操作,需等待其执行才能接着往下
  console.log('b');
});

server.use(async (ctx, next)=>{
  console.log('1111');
  await new Promise((resolve, reject)=>{
    setTimeout(function (){ resolve(); }, 1000);
  });
  console.log('2222');
});
// a - 1111 - 2222 - b

1.my-koa

const http=require('http');
//const Event=require('events').EventEmitter;
module.exports=class {
  constructor(){
    //this.__ev=new Event();
    this.__queue=[];
    this.__server=http.createServer((req, res)=>{
      const _this=this;
      if(this.__queue.length>0){
        //准备ctx对象
        const ctx={
          req, res,
          request: {},response: {body: ''}
        };
        _run(0);
        async function _run(n, cb){
          let fn=_this.__queue[n];
          let str=fn.constructor.toString();
          /*if(str.indexOf('GeneratorFunction')!=-1){
            console.log('生成器函数');
          }else */if(str.indexOf('AsyncFunction')!=-1){
            await fn(ctx, function (){
              //通过 promise + cb 回调方式控制 next() 中可能存在的异步操作!
              return new Promise((resolve, reject)=>{
                _run(n+1, function (){
                  resolve();
                });
              });
            });
          }else if(str.indexOf('Function')!=-1){
            fn(ctx, function (){
              _run(n+1);
            });
          }
          cb && cb();
        }
      }else{
        res.writeHeader(404);
        res.write('Not Found');
        res.end();
      }
    });
  }
  listen(port=80){
    this.__server.listen(port);
  }
  use(fn){
    let str=fn.constructor.toString();
    if(str.indexOf('GeneratorFunction')!=-1){
      console.warn('koa中generator已经抛弃了,你赶紧改改吧');
    }
    this.__queue.push(fn);
    //this.__ev.
    /*if(str.indexOf('GeneratorFunction')!=-1){
      console.log('生成器函数');
    }else if(str.indexOf('AsyncFunction')!=-1){
      console.log('Async函数');
    }else if(str.indexOf('Function')!=-1){
      console.log('普通函数');
    }*/
  }
};

2.guan-koa

const http = require("http")
module.exports = class {
    constructor() {
        // __ 双下划线表示约定的私有成员,外界不可使用
        const _this = this;
        this.__queue = [];	 //store functions from Koa.use
        this.__server = http.createServer((req, res) => {
            if (_this.__queue.length > 0) {
                const ctx = {
                    req, res,   
                    request: {},
                    response: {
                        body: ''
                    }
                };
                
                _run(0);
                async function _run(i){
                    const fn = _this.__queue[i];
                    const str = fn.constructor.toString();
                    if(str.indexOf("AsyncFunction") != -1){
                      //直接使用 async-await 方式控制 next() 中可能存在的异步操作 !!!
                        await fn(ctx,async function(){
                            await _run(i+1)
                        });
                    }else if(str.indexOf("Function") != -1){
                        fn(ctx,function(){
                            _run(i+1)
                        });
                    }
                }
            } else {
                res.writeHead(404);
                res.end("Not Found");
            }
        });
    }
    //use / listen 同 my-koa
}

4.practice

1.方便管理,将各种配置单独作为一个模块文件
2.各种异步操作,为了使用方便,通常会使用Promise封装起来,如:database / fs

1.server

const server=new koa();
server.listen(config.PORT);
server.use(async (ctx,next)=>{	//也可以使用koa2-cors
  ctx.set({'Access-Control-Allow-Origin': '*'});
  ctx.user_id='dfsdt43r3ewewerfhdft45';
  await next();
});
let router=new Router();	//koa-router
router.use('/api/', require('./routers/api.router'));
server.use(router.routes());
server.use(static(pathlib.resolve(__dirname, 'www/')));//koa-static
console.log(`Server running on: localhost:${config.PORT}`);

2.router

//routers/api.router
let router=new Router();
//collect
router.get('collect/:type/:data/', async ctx=>{
  let {type, data}=ctx.params;
  await db.insert('collect_table', {type, data});
  ctx.body={OK: true};
});
module.exports=router.routes();

3.database

const mysql=require('mysql');
const config=require('../config');
const assert=require('assert');
let db=mysql.createPool({
  host:       config.DB_HOST,
  port:       config.DB_PORT,
  user:       config.DB_USER,
  password:   config.DB_PASS,
  database:   config.DB_NAME,
});
//方便使用,用promise将各种异步操作封装起来
db._query=db.query;
db.query=function (sql){
  return new Promise((resolve, reject)=>{
    db._query(sql, (err, rows)=>{
      if(err){
        reject(err);
      }else{resolve(rows);}
    });
  });
};
db.select=function (table, fields, where){
  if(!where){
    where='1=1';
  }else{
    let arr=[];
    for(let key in where){
      arr.push(`${key}='${filterValue(where[key])}'`);
    }
    where=arr.join(' AND ');
  }
  return db.query(`SELECT ${fields} FROM ${table} WHERE ${where}`);
};
//insert / delete / update ......
module.exports=db;

4.fs

//为了方便使用,通常用promise将各种异步操作封装起来
module.exports={
  readFile(path, options){
    return new Promise((resolve, reject)=>{
      fs.readFile(path, options, (err, buffer)=>{
        if(err){
          reject(err);
        }else{
          resolve(buffer);
        }
      });
    });
  },
  writeFile(path, data, options){
    return new Promise((resolve, reject)=>{
      fs.writeFile(path, data, options, err=>{
        if(err){
          reject(err);
        }else{
          resolve();
        }
      });
    });
  }
}