Node中關於如何熱更新一個模塊

最近做了一些基於Egg的web項目,在開發階段,一般會在本地安裝nodemon這樣的工具,從而當我們改變一個文件時,他可以監聽到這個改動,然後重新啓動進程,將數據從硬盤裏重新加載到內存中,所以我們新的改動可以應用到應用程序中去。這裏不免想到一個問題,如果不重新啓動的話,我修改了一個文件,怎麼去應用最近的改動呢?

  1. 首先先簡單介紹一下require這個屬性,當我們編寫完Node.js代碼後,被解釋器執行時,會做一層包裝,像下面那樣
(function(module,exports,require,__dirname,__pathname ){
	// your code ...
})

所以我們可以直接在任何地方引用require這個方法,除了用來引入模塊以外,它上面還有一些額外的數據。我們先建立2個簡單的文件,在同一個目錄下

// entry.js
const lib = require('./lib');
const name = lib.getName();
console.log(name);
//lib.js
console.log('I got it');
const lib = {
    name: 'kasol',
    getName: function() {
        return this.name;
    }
};
module.exports = lib;

然後我們直接執行entry.js,結果很明顯

//I got it
//kasol
  1. 接下來我們做一點點簡單的改動
// entry.js
const lib = require('./lib');
const lib2 = require('./lib');
const lib3 = require('./lib');
const name = lib.getName();
console.log(name);

我在entry.js中,多次引用了lib,輸出的結果會怎麼樣呢?

//I got it
//kasol

是的,“I got it” 始終只輸出了一次,因爲Node在加載執行完一個模塊之後,會將它緩存下來,之後再有模塊想要去引用它時,就會直接從緩存中返回,所以這個console只執行了一次。

  1. 從上面看來,就算我們能夠在模塊改動後,重新 require對應的文件,還是會從緩存中去取,那麼也不能拿到最新的改動,所以要先解決怎麼讓他重新去加載的問題,這時候就要看上述提到的require.cache了。我們再來改改entry.js
//entry.js
const lib = require('./lib');
console.log(require.cache);

我們執行下,看看打印的結果

{ 
   Module {
     id: '/Users/zhongzeming/webpackDemo/test/lib.js',
     exports: { name: 'kasolll', getName: [Function: getName] },
     parent:
      Module {
        id: '.',
        exports: {},
        parent: null,
        filename: '/Users/zhongzeming/webpackDemo/test/entry.js',
        loaded: false,
        children: [Array],
        paths: [Array] },
     filename: '/Users/zhongzeming/webpackDemo/test/lib.js',
     loaded: true,
     children: [],
     paths:
      [ '/Users/zhongzeming/webpackDemo/test/node_modules',
        '/Users/zhongzeming/webpackDemo/node_modules',
        '/Users/zhongzeming/node_modules',
        '/Users/node_modules',
        '/node_modules' ] } }

簡單起見,我只貼出了lib模塊的信息,可以看到上面這個cache裏面是存在的,所以在第二次去require的時候,就去這裏拿了。

  1. 所以我們再改改entry
//entry.js
const lib = require('./lib');
console.log(require.cache);
delete require.cache[require.resolve('./lib.js')];
console.log(require.cache);

這時候會發現,這個cache裏面就不存在lib模塊的信息了,上面的代碼裏面,有一個delete require.cache[require.resolve(’./lib.js’)],require.resolve就是返回這個模塊的絕對路徑,作爲一個key值去cache裏面把這個模塊的信息給刪去,之後再去require的話,就又會去重新加載這個模塊了,最後我們把倆個文件都改改
如下

// entry.js
let lib = require('./lib');
const fs = require('fs');
const path = require('path');
fs.watch(path.join(__dirname, 'lib.js'), { encoding: 'utf-8' }, (eventType, filename) => {
    delete require.cache[require.resolve('./lib.js')];
    lib = require('./lib');
    console.log(lib.getName());
});


// lib.js
console.log('I got it');
const lib = {
    name: 'kasol',
    getName: function() {
        return this.name;
    }
};
module.exports = lib;

這裏用了fs.watch,作爲監聽文件改動來說可能不是那麼完美,但是我們這裏用來演示改動更新已經足夠了。先執行entry,然後可以不停的改動lib.js中的name值看看,就會發現console出來的值已經不一樣了。

總結

上面的代碼只是簡單的演示,雖然在生產環境中熱更新一個模塊文件好像並沒有什麼太大的意義,而在開發環境中,其實我們最常用的還是諸如nodemon這類的工具,不過,這也提供了一個思路,可以在不重新啓動進程的情況下,去重新加載一到個模塊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章