首发于easyjs

Egg + Webpack 热更新实现

1. 背景

在用 Node.js + Webpack 构建的方式进行开发时, 我们希望能实现修改代码能实时刷新页面 UI的效果. 这个特性 Webpack 本身是支持的, 而且基于koa也有现成的 koa-webpack-hot-middleware 和 koa-webpack-dev-middleware 封装好的组件支持.

不过这里如果需要支持Node.js服务器端修改代码自动重启webpack自动编译功能该如何实现呢, 主要存在以下几个问题:

  • 如何解决 Node.js 服务器端代码修改应用重启避免Webpack重新编译.
  • 如何访问 js,css,image等静态资源.
  • 服务端渲染时, Node 层如何读取 Webpack 内存编译的内容
  • 如何处理本地开发 Webpack 热更新内存存储读取和线上应用本机文件读取逻辑分离.2. 前端渲染和服务端渲染构建热更新实现


2. 前端渲染和服务端渲染构建热更新实现

在 koa 项目中, 通过 koa-webpack-dev-middleware 和 koa-webpack-hot-middleware 可以实现 webpack 编译内存存储和热更新功能, 代码如下:

const compiler = webpack(webpackConfig);
const devMiddleware = require('koa-webpack-dev-middleware')(compiler, options);
const hotMiddleware = require('koa-webpack-hot-middleware')(compiler, options);
app.use(devMiddleware);
app.use(hotMiddleware);

如果按照上面实现, 可以满足修改修改客户端代码实现webpack自动变编译和UI界面热更新的功能.但如果是修改 Node.js 服务器端代码重启后就会发现webpack会重新编译, 这不是我们要的效果.原因是因为middleware是依赖app的生命周期, 当app销毁时, 对应webpack compiler实例也就没有了, 重启时会重新执行middleware初始化工作.

那有没有办法保持 Webpack 编译实例呢? 针对这个我们可以通过 Egg 框架已经内置了 Worker 和 Agent 机制来实现 Webpack 内存编译功能和通信功能。


3. Egg Worker 和 Agent 通信

3.1 Worker 与 Agent 通信

  • 通过app.messenger.sendToAgent 向agent发送消息
  • 通过app.messenger.on 监听agent发送过来的消息
// app.webpack.fileSystem.readWebpackMemoryFile 方法实现
this.app.messenger.sendToAgent(Constant.EVENT_WEBPACK_FILE_MEMORY, 
{ filepath });

this.app.messenger.on(Constant.EVENT_WEBPACK_READ_FILE_MEMORY_CONTENT, 
data => { // 因为消息是异步,必须是发过去的文件名与收到的文件名相同
  if (filepath === data.filepath) { 
    resolve(data.fileContent);
  }
});


3.2 Agent 与 Worker 通信

  • 通过 agent.messenger.sendToApp 向app发送消息
  • 通过 app.messenger.on 监听agent发送过来的消息

Agent 监听到 Worker 发过来的 filepath, 从 Webpack 内存中读取文件内容, 然后通过 agent.messenger.sendToApp 把内容发送给 Worker。

agent.messenger.on(Constant.EVENT_WEBPACK_READ_FILE_MEMORY, data => {
 const fileContent = Utils.readWebpackMemoryFile(compiler, data.filepath);
 agent.messenger.sendToApp(Constant.EVENT_WEBPACK_READ_FILE_MEMORY_CONTENT, {
   fileContent,
 });
});

4. Egg框架中Webpack构建

  • 我们利用本地开发修改 Node 层代码修复重启时, 只会重启 Worker 进程, 不会重启 Agent进程的机制, 我们可以在Agent里面启动 Webpack 编译服务解决 Webpack compiler 实例问题.
  • 因为 Egg App 进程 和 Agent 进程是两个进程, 当 url 访问时, 我们通过 Worker 发送消息给 Agen t进程, 获取服务端渲染的文件内容, 然后 Agent 再发送消息给 Worker 解决文件读取问题.
  • 本地开发 Webpack 热更新内存存储读取和线上应用本机文件读取逻辑分离功能, 我们通过本地开发模式时, 通过读取 Webpack 内存内容覆盖本地文件读取的逻辑, 这样在开发模式和发布模式可以无缝对接.

4.1 Egg + Webpack 项目本地开发模式

  • 首先执行node index.js 或者 npm start 启动 Egg应用
  • 在Egg Agent 里面启动koa服务, 同时在koa服务里面启动Webpack编译服务
  • 挂载Webpack内存文件读取方法覆盖本地文件读取的逻辑
const filePath = path.isAbsolute(name) ? name : path.join(app.config.view.root[0], name);
const promise = app.webpack.fileSystem.readWebpackMemoryFile(filepath);
return co(function* () {
  const content = yield promise;
  return render(content, data)
});
  • Worker 监听 Webpack 编译状态, 检测 Webpack 编译是否完成, 如果未完成, 显示Webpack 编译 Loading, 如果编译完成, 自动打开浏览器。
  • Webpack 编译完成, Agent 发送消息给 Worker, Worker 检测到编译完成, 自动打开浏览器, Egg服务正式可用。


Egg + Webpack 热更新实现插件: egg-webpack 可以用于 Node 服务端或者前端项目开发。

发布于 2017-09-14

文章被以下专栏收录