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. 引用的模塊要以函數的形式動態使用,不能保存在變量中

坑點

1:注意父模塊的引用關係,可能造成 內存泄露
詳見 https://zhuanlan.zhihu.com/p/34702356
解決辦法是在decache掉子模塊後,也decache父模塊

注意,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)
      }
    });
  });
});
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章