前言

  • 在前面的对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 包安装
1
npm i koa express -D
  • koa 和 express 的基本模板。 koa 只能用 new 的方式创建
1
2
3
4
5
// koa
const Koa = require("koa");
// ... use code
const KoaApp = new Koa();
KoaApp.listen(8000);

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

1
2
3
4
5
// express
const Express = require("express");
// ... use code
const ExpressApp = Express();
ExpressApp.listen(8080);
  • 两者的 hello。 koa:
1
2
3
4
// use code
KoaApp.use(function(ctx, next) {
    ctx.body = "hello koa";
});

express:

1
2
3
4
// 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 异步中间件
1
2
3
4
5
ExpressApp.use(function(req, res, next) {
    setTimeout(function() {
        res.end("测试");
    }, 0);
});

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

  • koa 异步中间件
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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
1
2
3
4
5
6
7
8
9
module.exports = function(req, res, next) {
    setTimeout(function() {
        if (req.path === "/") {
            res.end("测试");
        } else {
            next();
        }
    }, 0);
};
  • express 使用
1
2
const test = require("./hello-test.js");
ExpressApp.use(test);
  • 修改到 koa 使用
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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对象。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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

1
2
3
4
5
6
7
8
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. 下篇写这些内容
  • npm上发布你的包以共享给其他人使用。
  • 添加测试用例
  • 添加travis-ci自动集成测试.