Webpack 作 Node.js 代碼熱替換, 第一步

這兩天爲了這個問題, Gitter 上問, Twitter 上問, GitHub 上問, 兩天沒反應
原來寫博客的 jlongster 不理我, 我也不知道 Webpack 做者的聯繫方式
最後在 Gitter 上發的消息他彷佛看到了, 就粗略地解釋了一遍, 醍醐灌頂啊...
https://github.com/webpack/docs/issues/45#issuecomment-149793458前端

Here is the process in short:node

  • Compile the server code with webpackreact

  • Use target: "node" or target: "async-node"webpack

  • Enabled HMR via --hot or HotModuleReplacementPlugingit

  • Use webpack/hot/poll or webpack/hot/signalgithub

  • The first polls the fs for updates (easy to use)web

  • The second listens for a process event to check for updates (you need a way to send the signal)npm

  • Run the bundle with node.json

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 的配置教程裏抄:
http://jlongster.com/Backend-Apps-with-Webpack--Part-II

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 對象作處理,
也就是手工監聽文件修改, 而後清楚模塊緩存, 從新掛載模塊
思路清晰考慮細緻, 雖然有點冗餘代碼, 仍是能夠一試:
http://fex.baidu.com/blog/2015/05/nodejs-hot-swapping/

另外一個彷佛是對 require.extensions 作了 hack, 增長了操做和事件
當模塊文件更新時, 對應模塊自動更新, 而且 emit 一個事件
經過這樣的效果, 模塊引用的位置能夠作一些處理, 使用新的代碼
這個應該說仍是比較粗暴的, 畢竟不是全部的代碼都容易替換
https://github.com/rlidwka/node-hotswap

感想

考慮到我已經在 Webpack 這棵樹上吊死, 也就不打算深刻研究了
也許 Node.js 官方對 lib/module.js 作下優化能搞出不錯的功能來
然而, JavaScript 畢竟不是不可變數據使用成風的社區, 比不了 Erlang
由於代碼替換就涉及到狀態更新的問題, 很差搞, 不如重啓來得省事
而重啓如今有 node-dev supervisor nodemon 三套方案任你選

對我來講, 主要是 Cumulo 方案對 WebSocket 存在巨大的依賴
如今前端開發已經能作到服務器上更新代碼, 客戶端自動更新了,
經過 Webpack 和 React 的機制, 局部更新 DOM 和純函數模塊
若是說可以作到開發環境也能熱替換, 這對於開發效率的提高就太大了
原本以爲熱替換高不可攀的, 然而極可能是觸手可及的效率提高!

後面大概還有坑, 畢竟黑科技... 遇到再說了
有興趣能夠細看下 jlongster 寫的相關的幾篇神做, 很是有幫助:
http://jlongster.com/archive

相關文章
相關標籤/搜索