早期學習 Node.js 的時候 (2011-2012),有挺多是從 PHP 轉過來的,當時有部分人對于 Node.js 編輯完代碼需要重啟一下表示麻煩(PHP不需要這個過程),于是社區里的朋友就開始提倡使用 node-supervisor 這個模塊來啟動項目,可以編輯完代碼之后自動重啟。不過相對于 PHP 而言依舊不夠方便,因為 Node.js 在重啟以后,之前的上下文都丟失了。
雖然可以通過將 session 數據保存在數據庫或者緩存中來減少重啟過程中的數據丟失,不過如果是在生產的情況下,更新代碼的重啟間隙是沒法處理請求的(PHP可以,另外那個時候 Node.js 還沒有 cluster)。由于這方面的問題,加上本人是從 PHP 轉到 Node.js 的,于是從那時開始思考,有沒有辦法可以在不重啟的情況下熱更新 Node.js 的代碼。
最開始把目光瞄向了 require 這個模塊。想法很簡單,因為 Node.js 中引入一個模塊都是通過 require 這個方法加載的。于是就開始思考 require 能不能在更新代碼之后再次 require 一下。嘗試如下:
a.js
var express = require('express');var b = require('./b.js'); var app = express();app.get('/', function (req, res) { b = require('./b.js'); res.send(b.num); });app.listen(3000);b.js
exports.num = 1024;
兩個 JS 文件寫好之后,從 a.js 啟動,刷新頁面會輸出 b.js 中的 1024,然后修改 b.js 文件中導出的值,例如修改為 2048。再次刷新頁面依舊是原本的 1024。
再次執行一次 require 并沒有刷新代碼。require 在執行的過程中加載完代碼之后會把模塊導出的數據放在 require.cache 中。require.cache 是一個 { } 對象,以模塊的絕對路徑為 key,該模塊的詳細數據為 value。于是便開始做如下嘗試:
a.js
var path = require('path');var express = require('express');var b = require('./b.js'); var app = express();app.get('/', function (req, res) { if (true) { // 檢查文件是否修改 flush(); } res.send(b.num); });function flush() { delete require.cache[path.join(__dirname, './b.js')]; b = require('./b.js'); }app.listen(3000);再次 require 之前,將 require 之上關于該模塊的 cache 清理掉后,用之前的方法再次測試。結果發現,可以成功的刷新 b.js 的代碼,輸出新修改的值。
了解到這個點后,就想通過該原理實現一個無重啟熱更新版本的 node-supervisor。在封裝模塊的過程中,出于情懷的原因,考慮提供一個類似 PHP 中 include 的函數來代替 require 去引入一個模塊。實際內部依舊是使用 require 去加載。以b.js為例,原本的寫法改為 var b = include(‘./b'),在文件 b.js 更新之后 include 內部可以自動刷新,讓外面拿到最新的代碼。
但是實際的開發過程中,這樣很快就碰到了問題。我們希望的代碼可能是這樣:
新聞熱點
疑難解答
圖片精選