为koa移植express中间件

前言

  • 在前面的对vue-ssr改造为koaweb框架,我使用了一个第三方npm库。

  • 包名为 koa2-webpack-middleware-zm 已迁移到koa-webpack-middleware-zm

  • 这个包是我自己因为ssr的特殊需求github上并没有合适的包。

  • 所以自行参考了koa-webpack-middleware后写出的包。

  • 并且修复原有包的一些 bug。

  • 这篇博文我将写以下内容

  • koa 中间件的编写。

  • webpack-dev-middleware这种express中间件改造为一个koa中间件。

一. koa与express的普通中间件区别。

  • npm 包安装

npm i koa express -D
  • koa和express的基本模板。
    koa只能用new的方式创建

// koa
const Koa = require('koa')
// ... use code
const KoaApp = new Koa()
KoaApp.listen(8000)

express可以用方法调用或new的方式创建

// express
const Express = require('express')
// ... use code
const ExpressApp = Express()
ExpressApp.listen(8080)
  • 两者的hello。
    koa:

// use code
KoaApp.use(function(ctx, next){
    ctx.body = 'hello koa'
})

express:

// use code
ExpressApp.use(function(req, res, next){
    res.end('hello exress')
})
  • express 中间件运行逻辑

  1. 中间件为一个方法接受 req,res,next 三个参数。

  2. 中间可以执行任何方法包括异步方法。

  3. 最后一定要通过res.end或者next来通知结束这个中间件方法。

  4. 如果没有执行res.end或者next访问会一直卡着不动直到超时。

  5. 并且在这之后的中间件也会没法执行到。

  • koa 的中间件运行逻辑

  1. 中间件为一个方法或者其它,这里就讲方法的,接受ctx,next两个参数。

  2. 方法中可以执行任何同步方法。可以使用返回一个Promise来做异步。

  3. 中间件通过方法结束时的返回来判断是否进入下一个中间件。

  4. 返回一个Promise对象koa会等待异步通知完成。then中可以返回next()来跳转到下一个中间件。

  5. 相同如果Promise没有异步通知也会卡住。

二. 异步中间件的区别

  • express 异步中间件

ExpressApp.use(function(req, res, next){
    setTimeout(function(){
        res.end('测试')
    }, 0)
})

express 的异步就是最普通的回调

  • koa 异步中间件

KoaApp.use(function(ctx, next){
    return new Promise(function(resolve, reject) {
        if (ctx.path === '/'){
            ctx.body = 'hello koa'
            resolve()
        } else {
            reject()
        }
    }).catch(next)
})

koa 的异步通过Promise来做这里我then不写代表resolve不切换到下一个中间件。
catch直接绑定next,用reject来通知跳转到下一个中间件。

三. 修改一个express中间件到koa

  • hello-test.js

module.exports = function(req, res, next){
    setTimeout(function(){
        if (req.path === '/'){
            res.end('测试')
        }else{
            next()
        }
    }, 0)
}
  • express 使用

const test = require('./hello-test.js')
ExpressApp.use(test)
  • 修改到 koa 使用

const test = require('./hello-test.js')
KoaApp.use(function (ctx, next){
    const res = ctx.res
    const req = ctx.req
    const end = res.end
    return new Promise(function(resolve, reject) {
        res.end = function () {
            end.apply(this, arguments)
            resolve()
        }
        test(res, req, reject)
    }).catch(next)
})

通过修改原有的res.end运行resolve通知Promise结束,
修改nextreject替代通知Promise调用next

四. 修改express注意事项

  1. 原有的express组件是通过回调来通知结束的。不要直接await或者yield一个组件。它们又不是返回一个Promise对象。

const test = require('./hello-test.js')
KoaApp.use(function *(next){
    const res = this.res
    const req = this.req
    // 这种写法会导致后面注册的中间件都失效。
    yield test(res, req, next)
})
KoaApp.use(async function (ctx, next){
    const res = ctx.res
    const req = ctx.req
    // 这种写法会导致后面注册的中间件都失效。
    await test(res, req, next)
})
  1. 只有在catch或者是then中返回next()才能跳转到下一个组件。

五. 改造webpack-dev-middleware的例子

devMiddleware.js

function koaDevMiddleware(expressDevMiddleware) {
    return function middleware(ctx, next) {
        return new Promise((resolve) => {
            expressDevMiddleware(ctx.req, {
                end: (content) => {
                    ctx.body = content;
                    resolve(false);
                },
                setHeader: (name, value) => {
                    ctx.set(name, value);
                },
            }, () => {
                resolve(true);
            });
        }).then((err) => {
            if (err) { return next(); }
            return null;
        });
    };
}
module.exports = koaDevMiddleware;

这是昨天最新的代码当时没想着用reject来通知next后面大概要改成这样。

六. 改造webpack-hot-middleware的例子

hotMiddleware.js

function koaHotMiddleware(expressHotMiddleware) {
    return function middleware(ctx, next) {
        return new Promise((resolve) => {
            expressHotMiddleware(ctx.req, ctx.res, resolve);
        }).then(next);
    };
}
module.exports = koaHotMiddleware;

七. 例子源码

  1. koa-webpack-middleware-zm

  2. 欢迎star,issues,fork, pr

  3. 原文地址

  4. 下篇博文写

  • npm上发布你的包以共享给其他人使用。

  • 添加测试用例

  • 添加travis-ci自动集成测试.

koa

相关推荐