ZEROMAKE | keep codeing and thinking!
2017-03-01 | middleware

为 koa 定制 webpack 的中间件

前言

  • 在前面的对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 的方式创建
1
// koa
2
const Koa = require("koa");
3
// ... use code
4
const KoaApp = new Koa();
5
KoaApp.listen(8000);

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

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

express:

1
// use code
2
ExpressApp.use(function(req, res, next) {
3
res.end("hello exress");
4
});
  • 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
ExpressApp.use(function(req, res, next) {
2
setTimeout(function() {
3
res.end("测试");
4
}, 0);
5
});

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

  • koa 异步中间件
1
KoaApp.use(function(ctx, next) {
2
return new Promise(function(resolve, reject) {
3
if (ctx.path === "/") {
4
ctx.body = "hello koa";
5
resolve();
6
} else {
7
reject();
8
}
9
}).catch(next);
10
});

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

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

  • hello-test.js
1
module.exports = function(req, res, next) {
2
setTimeout(function() {
3
if (req.path === "/") {
4
res.end("测试");
5
} else {
6
next();
7
}
8
}, 0);
9
};
  • express 使用
1
const test = require("./hello-test.js");
2
ExpressApp.use(test);
  • 修改到 koa 使用
1
const test = require("./hello-test.js");
2
KoaApp.use(function(ctx, next) {
3
const res = ctx.res;
4
const req = ctx.req;
5
const end = res.end;
6
return new Promise(function(resolve, reject) {
7
res.end = function() {
8
end.apply(this, arguments);
9
resolve();
10
};
11
test(res, req, reject);
12
}).catch(next);
13
});

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

四. 修改 express 注意事项

  1. 原有的express组件是通过回调来通知结束的。不要直接await或者yield一个组件。它们又不是返回一个Promise对象。
1
const test = require("./hello-test.js");
2
KoaApp.use(function*(next) {
3
const res = this.res;
4
const req = this.req;
5
// 这种写法会导致后面注册的中间件都失效。
6
yield test(res, req, next);
7
});
8
KoaApp.use(async function(ctx, next) {
9
const res = ctx.res;
10
const req = ctx.req;
11
// 这种写法会导致后面注册的中间件都失效。
12
await test(res, req, next);
13
});
  1. 只有在catch或者是then中返回next()才能跳转到下一个组件。

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

devMiddleware.js

1
function koaDevMiddleware(expressDevMiddleware) {
2
return function middleware(ctx, next) {
3
return new Promise(resolve => {
4
expressDevMiddleware(
5
ctx.req,
6
{
7
end: content => {
8
ctx.body = content;
9
resolve(false);
10
},
11
setHeader: (name, value) => {
12
ctx.set(name, value);
13
}
14
},
15
() => {
16
resolve(true);
17
}
18
);
19
}).then(err => {
20
if (err) {
21
return next();
22
}
23
return null;
24
});
25
};
26
}
27
module.exports = koaDevMiddleware;

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

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

hotMiddleware.js

1
function koaHotMiddleware(expressHotMiddleware) {
2
return function middleware(ctx, next) {
3
return new Promise(resolve => {
4
expressHotMiddleware(ctx.req, ctx.res, resolve);
5
}).then(next);
6
};
7
}
8
module.exports = koaHotMiddleware;

七. 例子源码

  1. koa-webpack-middleware-zm
  2. 欢迎 star,issues,fork, pr
  3. 下篇写这些内容
  • npm上发布你的包以共享给其他人使用。
  • 添加测试用例
  • 添加travis-ci自动集成测试.