这两天为了这个问题, Gitter 上问, Twitter 上问, GitHub 上问, 两天没反应
原来写博客的 jlongster 不理我, 我也不知道 Webpack 作者的联系方式
最后在 Gitter 上发的消息他似乎看到了, 就粗略地解释了一遍, 醍醐灌顶啊...
Here is the process in short: Compile the server code with webpack Use target: "node" or target: "async-node" Enabled HMR via --hot or HotModuleReplacementPlugin Use webpack/hot/poll or webpack/hot/signal The first polls the fs for updates (easy to use) The second listens for a process event to check for updates (you need a way to send the signal) Run the bundle with node. You can't use existing HMR loaders like react-hot-loader or style-loader because they make no sense in a server environment. Just add manuall replacement code at the correct location (i. e. accept request handler like in the example) You can't use the webpack-dev-server. It's a server which serves assets not a runner. Just run webpack --watch and node bundle.js. I would go the webpack/hot/poll?1000 route first. It's pretty easy to use and suitable for dev environments. For production (if you want to hot update your production server) the signal approach is better suited.
原话就不翻译了, 理解之后主要就是 Webpack 怎么配置和脚本怎么运行
我写了一遍, 代码仅仅是这么短, 热替换就实现了:
https://github.com/jiyinyiyong/webpack-backend-HMR-demo
其中代码可以从 jlongster 的配置教程里抄:
webpack = require 'webpack' module.exports = entry: [ 'webpack/hot/poll?1000' # <-- 轮询更新内容的代码 './src/main' # <-- 项目入口 ] target: 'node' # <-- 指明编译方式为 node output: path: 'build/' filename: 'bundle.js' # <-- 编译结果的文件名 module: loaders: [ {test: /\.coffee/, loader: 'coffee'} ] plugins: [ new webpack.HotModuleReplacementPlugin() # <-- 照常启动 hot mode ] resolve: extensions: ['.js', '', '.coffee']
命令行环境运行的话, 注意是 webpack 而不是 webpack-dev-server
注意后台运行的 & 只是为了不阻塞, 你有两个终端就开两个吧
npm i webpack --watch & # <-- watch 模式 node build/bundle.js # <-- 运行的是打包结果的代码
我写了两个测试文件, 一个是会修改的代码 src/lib.coffee:
exports.data = 'code 5' exports.printSelf = -> console.log 'doing 3'
另一个入口文件 src/main.coffee 包含了处理模块替换的代码:
lib = require './lib' console.log lib.data lib.printSelf() counter = 0 setInterval -> counter += 1 console.log counter , 2000 if module.hot module.hot.accept './lib', -> lib = require './lib' console.log lib.data lib.printSelf()
跑一跑 Demo, 就知道效果怎么样了, setInterval 不受替换的干扰
而在 build/ 目录, 每次修改都会生成一个 JSON 文件记录修改的内容:
复制代码 代码如下:
➤➤ l build/
0.1dadeb2eb7b01e150126.hot-update.js 0.c1d0d73de39660806d0c.hot-update.js 2849b61a15d31ffe5e08.hot-update.json 0.99ea3ea7633f6b3750e6.hot-update.js 0.eaa7b323eba37ae58997.hot-update.js 9b4a5ad617ec1dbc48a3.hot-update.json fb584971920454f9ccbe.hot-update.json
0.9abf25005c61357a0ce5.hot-update.js 0.fb584971920454f9ccbe.hot-update.js a664b5851a99ac0865ca.hot-update.json
0.9b4a5ad617ec1dbc48a3.hot-update.js 1dadeb2eb7b01e150126.hot-update.json bundle.js
0.a664b5851a99ac0865ca.hot-update.js 256267122c6d325755b0.hot-update.json c1d0d73de39660806d0c.hot-update.json
具体的文件内容也就是这样, 大致可以认为包含了识别更新所需的信息:
➤➤ cat build/0.c797c084381bfeac37f7.hot-update.js exports.id = 0; exports.modules = { /***/ 3: /***/ function(module, exports, __webpack_require__) { var counter, lib; lib = __webpack_require__(4); console.log(lib.data); lib.printSelf(); counter = 0; setInterval(function() { counter += 1; return console.log(counter, 3); }, 2000); if (true) { module.hot.accept(4, function() { lib = __webpack_require__(4); console.log(lib.data); return lib.printSelf(); }); } /***/ } };
其他方案
白天在网上查找方案, 顺便在论坛上发了个帖子问这个事情,现成的主要两个说明比较清楚的方案, 值得借鉴一下
一个是百度的技术博客上, 写的大概是怎么对 module 对象做处理,也就是手工监听文件修改, 然后清楚模块缓存, 重新挂载模块
思路清晰考虑细致, 虽然有点冗余代码, 还是可以一试:
https://www.jb51.net/article/73739.htm