1.koa/express区别
-
更轻量,无内置
static,需要依赖koa-static -
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();
}
});
});
}
}