前端全棧必會node框架koa。。。

嗯_嗯發表於2018-08-07

啥是koa?那個美女好像也叫koa

Koa2是現在最流行的基於Node.js平臺的web開發框架;Koa 應用程式是一個包含一組中介軟體函式的物件,它是按照類似堆疊的方式組織和執行的。

const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
  ctx.body = 'Hello World';
});
app.on('error', err => {
  log.error('server error', err)
});

app.listen(3000);
複製程式碼

koa中介軟體以更傳統的方式級聯;那和express的中介軟體有什麼區別呢?

  • 區別只有一點express的中介軟體中存在非同步操作時不會等待;而koa會等待非同步操作完成後;才會執行下一個中介軟體;

express和koa的區別

let express = require('express');
let app = express();
app.use(async (req, res, next) => {
  console.log(1);
  await next();
  console.log(2);
});
app.use(async (req, res, next) => {
  console.log(3);
  await new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('非同步');
      resolve()
    }, 100);
  })
  await next();
  console.log(4);

});
app.listen(3000);
// 1 3 2 非同步 4  
//遇到非同步不會等待 直接執行;打亂原來的洋蔥模型

複製程式碼
  • koa
let Koa = require('Koa');
let 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 new Promise((resolve, reject) => {
          setTimeout(() => {
              console.log('非同步');
              resolve()
          }, 100);
      })
    await next();
    console.log(4)
})
app.listen(3000);
// 1 3 非同步 4 2  

複製程式碼

koa使用中介軟體獲取請求體

const bodyParser = require('koa-bodyparser');
app.use(bodyParser());
app.use(async (ctx)=>{
    let body=ctx.request.body;//得到請求體
})

複製程式碼

使用中介軟體獲取包含檔案的請求體

const bodyParser = require('koa-better-body');
et convert = require('koa-convert'); // 將1.0的中介軟體 轉化成2.0中介軟體
app.use(convert(bodyParser({
    uploadDir: path.join(__dirname, 'uploads'),
    keepExtensions: true
})));
app.use(async (ctx)=>{
    let body=ctx.request.fields;//得到檔案資訊
})
複製程式碼

路由中介軟體

//一級路由
const Router = require('koa-router');
let user = new Router();
user.get('/user', function (ctx) {
    ctx.body = 'get user ';
}).get('/query/:id', function (ctx) {
    ctx.body = ctx.params;
}).post('/user', function (ctx) {
    ctx.body = 'post user ';
}).get('/home', function (ctx) {
    ctx.body = 'get home ';
});
app.use(user.routes());

//多級路由
let user = new Router();
user.get('/add', function (ctx) {
    ctx.body = 'get user add ';
});

let article = new Router();
article.get('/add', function (ctx) {
    ctx.body = 'get article add ';
});

let router = new Router();
router.use('/user', user.routes()); //  /user/add
router.use('/article', article.routes()); ///article/add
app.use(router.routes());
複製程式碼

koa自帶存取cookie方法

//ctx.cookies.get(name,[optins]):讀取上下文請求中的cookie。
//ctx.cookies.set(name,value,[options]):在上下文中寫入cookie。
app.keys = ['hello'];
ctx.cookies.set('name','sss',{
    domain:'localhost', // 在哪個域名下設定cookie
    path:'/',// 在哪個路徑下設定cookie
    maxAge:10*1000, // 最大存活時間
    httpOnly:false, //是否只用http請求中獲得
    overwrite:true,//是否允許重寫
    signed:true//簽名要app.keys 
  });

expires:cookie失效時間

複製程式碼

session

let Koa = require('koa');
let app = new Koa();
let Router = require('koa-router');
const session = require('koa-session');
app.keys = ['key']
app.use(session({},app)); // 用了這個中介軟體 可以在ctx上增加session屬性
router.get('/cross', (ctx,next)=> {
  let n = ctx.session.n || 0;
  ctx.session.n = ++n;
  ctx.body = ctx.session.n;
});
app.use(router.routes());
app.use(router.allowedMethods()); // 405
複製程式碼

模板引擎

let Koa = require('koa');
let app = new Koa();
let Router = require('koa-router');
let router = new Router();
let views = require('koa-views');
app.use(views(__dirname, {
  map:{'html':'ejs'}//識別HTML
}));
router.get('/',async (ctx,next)=>{
  // 如果不寫return 這個函式執行完就結束了 模板還沒有被渲染,ctx.body = ''
  // 如果使用return會等待這個返回的promise執行完後才把當前的promise完成
   return ctx.render('ejs.html',{title:'標題'});
})
app.use(router.routes());
app.listen(3000); 

複製程式碼

靜態資源中介軟體

const static = require('koa-static')
app.use(static(path.join( __dirname,  'public')))
app.use( async ( ctx ) => {
  ctx.body = 'Not Found'
})
複製程式碼

自己實現koa

let EventEmitter = require('events');
let http = require('http');
let context = require('./context');
let request = require('./request');
let response = require('./response');
class Koa extends EventEmitter{
  constructor(){
    super();
    this.middlewares = [];
    this.context = context;
    this.request = request;
    this.response = response;
  }
  use(fn){
    this.middlewares.push(fn);
  }
  createContext(req,res){ 
    let ctx = Object.create(this.context); 
    let request = Object.create(this.request); 
    let response = Object.create(this.response);
    ctx.request = request; 
    ctx.response = response; 
    ctx.req = ctx.request.req =  req;
    ctx.res = ctx.response.res =  res;
    return ctx;
  }
  compose(middlewares,ctx){
    // express
    function dispatch(index) {
      if (index === middlewares.length) return Promise.resolve();
      let route = middlewares[index];
      // 把每個中介軟體都包裝成promise
      return Promise.resolve(route(ctx, () => dispatch(index+1)));
    }
    return dispatch(0);
  }
  handleRequest(req,res){
    let ctx = this.createContext(req,res);
    res.statusCode = 404;
    // this.fn(context); 需要把中介軟體函式組合起來

    let fn = this.compose(this.middlewares,ctx);
    let Stream = require('stream');
    fn.then(data=>{
      if(typeof ctx.body == 'object'){
        res.setHeader('Content-Type', 'application/json;charset=utf8');
        res.end(JSON.stringify(ctx.body))
      } else if (ctx.body instanceof Stream){
        ctx.body.pipe(res);
      }
      else if (typeof ctx.body === 'string' || Buffer.isBuffer(ctx.body)) {
        res.setHeader('Content-Type', 'text/html;charset=utf8');
        res.end(ctx.body);
      } else {
        res.end('Not found')
      }
    }).catch(err=>{
      this.emit('error',err);//
      res.statusCode = 500;
      res.end(`server error`);
    })
  }
  listen(){
    let server = http.createServer(this.handleRequest.bind(this));
    server.listen(...arguments);
  }
}

module.exports = Koa;


//ctx
let proto = {}
// 代理的作用
function defineGetter(property,name) {
  proto.__defineGetter__(name,function () {
    return this[property][name];
  })
}
function defineSetter(property,name) {
  // ctx.body = xxx
  // ctx.response.body = xxx
  proto.__defineSetter__(name,function (val) {
    this[property][name] = val;
  })
}
// ctx.query = ctx.request.query
defineGetter('request','query');
defineGetter('response','body');
defineSetter('response','body');
module.exports = proto;


//request
let request = { // 封裝求的方法
  get url(){
    return this.req.url
  },
  get path(){
    let {pathname:path} = require('url').parse(this.req.url);
    return path;
  },
  get query(){
    let { query } = require('url').parse(this.req.url,true);
    return query;
  }
  // .........
}

module.exports = request;


//response
let response = {
  get body(){
    return this._body;
  },
  set body(value){
    this.res.statusCode = 200;
    this._body = value;
  }
}
module.exports = response;
複製程式碼

相關文章