1. 程式人生 > >node.js 的 模組載入機制和server端hot reload熱載入實踐

node.js 的 模組載入機制和server端hot reload熱載入實踐

工作上遇到個後臺的專案,express + vue 寫的,用了Webpack Hot Middleware配合webpack-dev-middleware來讓客戶端連線到客戶端,使用nodemon來監聽重啟伺服器。但是webpack打包前端靜態資源的速度還是比較慢的,每一次對server端的改動都要等待重新打包,但客戶端的檔案其實並沒有改動,這麼做就浪費了時間。所以,想摸索一下伺服器端熱載入的方法,不再用nodemon監聽,在改動server端程式碼的時候,前端該幹嘛幹嘛,不要摻和進來重新打包了。

想法來源

node.js的模組載入機制是commonjs的,與es6的export/import有所區別。雖然babel可以把它轉成es6的語法,但本質不變。可以通過刪除require.cache來去除快取的node module。
仿照了

這個例項ultimate-hot-reloading的思路,不過例子的檔案結構很簡單,實際專案中模組引用可能一層套一層的,還是有些地方需要注意。

基本思路

  1. 監聽檔案變動(chokidar, fs.watchFile)
    使用chokidar來監聽檔案的變動
  2. 在變動的範圍內去快取decache ,可以採用外掛如clear-require, recache 等, 也可以手動 decache )
  3. 引用的模組要以函式的形式動態使用,不能儲存在變數中

坑點

注意,server啟動之後,是不能直接去掉app.js檔案的快取的。
2. 不再用nodemon或者supervisor監聽全部檔案。如果不改動app.js的話,用Node直接啟動。若用import/export 代替了 require 寫法,啟動程式時用Babel-node 代替node,也可在配置裡改babel。

        npm i —save-dev babel-cli

3: 動態引入模組,否則該模組會以變數的形式快取在父模組中,及時去掉了該模組的快取,因為父模組沒變,等於並沒有重新載入。通常做法是在app.js中動態引用路由。通過加入外掛dynamic-import來完成,寫成await import(‘moduleYouNeed’)的形式。

最佳實踐

最佳實踐應該是仍然用nodemon啟動伺服器,通過配置nodemon.json,只監聽app.js,對其他伺服器端的檔案ignore,而用delete require.cache[id]這種方式在webpack-dev-middleware和webpack-hot-middleware被使用之前監聽和去掉其餘server端的檔案。

主體程式碼

const watcher = chokidar.watch(path.resolve(__dirname, './routes'))
  watcher.on('ready', function() {
    console.log('will watching at ', path.resolve(__dirname, './routes'))
    watcher.on('all', function(event, file) {
      Object.keys(require.cache).forEach(function(id) {
        if(/\/www\//.test(id)) {
          console.log('before decache\n', id)
          delete require.cache[file] 
        }
      });
      logger.info('originPath', file, '\n')
    Object.keys(require.cache).forEach(function(id) {
      if(/\/www\//.test(id)) {
        console.log('after\n', id)
      }
    });
  });
});