# 模塊
* [`[Basic]` 模塊機制](#模塊機制)
* [`[Basic]` 熱更新](#熱更新)
* [`[Basic]` 上下文](#上下文)
* [`[Basic]` 包管理](#包管理)
## 常見問題
> <a name="q-hot"></a> 如何在不重啟 node 進程的情況下熱更新一個 js/json 文件? 這個問題本身是否有問題?
可以清除掉 `require.cache` 的緩存重新 `require(xxx)`, 視具體情況還可以用 VM 模塊重新執行.
當然這個問題可能是典型的 [`X-Y Problem`](http://coolshell.cn/articles/10804.html), 使用 js 實現熱更新很容易碰到 v8 優化之后各地拿到緩存的引用導致熱更新 js 沒意義. 當然熱更新 json 還是可以簡單一點比如用讀取文件的方式來熱更新, 但是這樣也不如從 redis 之類的數據庫中讀取比較合理.
## 簡述
其他還有很多內容也是屬于很 '基礎' 的 Node.js 問題 (例如異步/線程等等), 但是由于歸類的問題并沒有放在這個分類中. 所以這里只簡單講幾個之后沒歸類的基礎問題.
## 模塊機制
node 的基礎中毫無疑問的應該是有關于模塊機制的方面的, 也即 `require` 這個內置功能的一些原理的問題.
關于模塊互相引用之類的, 不了解的推薦先好好讀讀[官方文檔](https://nodejs.org/dist/latest-v6.x/docs/api/modules.html).
其實官方文檔已經說得很清楚了, 每個 node 進程只有一個 VM 的上下文, 不會跟瀏覽器相差多少, 模塊機制在文檔中也描述的非常清楚了:
```javascript
function require(...) {
var module = { exports: {} };
((module, exports) => {
// Your module code here. In this example, define a function.
function some_func() {};
exports = some_func;
// At this point, exports is no longer a shortcut to module.exports, and
// this module will still export an empty default object.
module.exports = some_func;
// At this point, the module will now export some_func, instead of the
// default object.
})(module, module.exports);
return module.exports;
}
```
> <a name="q-global"></a> 如果 a.js require 了 b.js, 那么在 b 中定義全局變量 `t = 111` 能否在 a 中直接打印出來?
① 每個 `.js` 能獨立一個環境只是因為 node 幫你在外層包了一圈自執行, 所以你使用 `t = 111` 定義全局變量在其他地方當然能拿到. 情況如下:
```javascript
// b.js
(function (exports, require, module, __filename, __dirname) {
t = 111;
})();
// a.js
(function (exports, require, module, __filename, __dirname) {
// ...
console.log(t); // 111
})();
```
> <a name="q-loop"></a> a.js 和 b.js 兩個文件互相 require 是否會死循環? 雙方是否能導出變量? 如何從設計上避免這種問題?
② 不會, 先執行的導出空對象, 通過導出工廠函數讓對方從函數去拿比較好避免. 模塊在導出的只是 `var module = { exports: {} };` 中的 exports, 以從 a.js 啟動為例, a.js 還沒執行完 exports 就是 `{}` 在 b.js 的開頭拿到的就是 `{}` 而已.
另外還有非常基礎和常見的問題, 比如 module.exports 和 exports 的區別這里也能一并解決了 exports 只是 module.exports 的一個引用. 沒看懂可以在細看我以前發的[帖子](https://cnodejs.org/topic/5734017ac3e4ef7657ab1215).
再晉級一點, 眾所周知, node 的模塊機制是基于 [`CommonJS`](http://javascript.ruanyifeng.com/nodejs/module.html) 規范的. 對于從前端轉 node 的同學, 如果面試官想問的難一點會考驗關于 [`CommonJS`](http://javascript.ruanyifeng.com/nodejs/module.html) 的一些問題. 比如比較 `AMD`, `CMD`, [`CommonJS`](http://javascript.ruanyifeng.com/nodejs/module.html) 三者的區別, 包括詢問關于 node 中 `require` 的實現原理等.
## 熱更新
從面試官的角度看, `熱更新` 是很多程序常見的問題. 對客戶端而言, 熱更新意味著不用換包, 當然也包含著 md5 校驗/差異更新等復雜問題; 對服務端而言, 熱更新意味著服務不用重啟, 這樣可用性較高<del>同時也優雅和有逼格</del>. 問的過程中可以一定程度的暴露應聘程序員的水平.
從 PHP 轉 node 的同學可能會有些想法, 比如 PHP 的代碼直接刷上去就好了, 并沒有所謂的重啟. 而 node 重啟看起來動作還挺大. 當然這里面的區別, 主要是與同時有 PHP 與 node 開發經驗的同學可以討論, 也是很好的切入點.
在 Node.js 中做熱更新代碼, 牽扯到的知識點可能主要是 `require` 會有一個 `cache`, 有這個 `cache` 在, 即使你更新了 `.js` 文件, 在代碼中再次 `require` 還是會拿到之前的編譯好緩存在 v8 內存 (code space) 中的的舊代碼. 但是如果只是單純的清除掉 `require` 中的 `cache`, 再次 `require` 確實能拿到新的代碼, 但是這時候很容易碰到各地維持舊的引用依舊跑的舊的代碼的問題. 如果還要繼續推行這種熱更新代碼的話, 可能要推翻當前的架構, 從頭開始從新設計一下目前的框架.
不過熱更新 json 之類的配置文件的話, 還是可以簡單的實現的, 更新 `require` 的 `cache` 可以實現, 不會有持有舊引用的問題, 可以參見我 2 年前寫著玩的[例子](https://www.npmjs.com/package/auto-reload), 但是如果舊的引用一直被持有很容易出現內存泄漏, 而要熱更新配置的話, 為什么不存數據庫? 或者用 `zookeeper` 之類的服務? 通過更新文件還要再發布一次, 但是存數據庫直接寫個接口配個界面多爽你說是不是?
所以這個問題其實本身其實是值得商榷的, 可能是典型的 [`X-Y Problem`](http://coolshell.cn/articles/10804.html), 不過聊起來確實是可以暴露水平.
## 上下文
如果你已經了解 ①② 那么你也應該了解, 對于 Node.js 而言, 正常情況下只有一個上下文, 甚至于內置的很多方面例如 `require` 的實現只是在啟動的時候運行了[內置的函數](https://github.com/nodejs/node/tree/master/lib).
每個單獨的 `.js` 文件并不意味著單獨的上下文, 在某個 `.js` 文件中污染了全局的作用域一樣能影響到其他的地方.
而目前的 Node.js 將 VM 的接口暴露了出來, 可以讓你自己創建一個新的 js 上下文, 這一點上跟前端 js 還是區別挺大的. 在執行外部代碼的時候, 通過創建新的上下文沙盒 (sandbox) 可以避免上下文被污染:
```javascript
'use strict';
const vm = require('vm');
let code =
`(function(require) {
const http = require('http');
http.createServer( (request, response) => {
response.writeHead(200, {'Content-Type': 'text/plain'});
response.end('Hello World\\n');
}).listen(8124);
console.log('Server running at http://127.0.0.1:8124/');
})`;
vm.runInThisContext(code)(require);
```
這種執行方式與 eval 和 Function 有明顯的區別. 關于 VM 更多的一些接口可以先閱讀[官方文檔 VM (虛擬機)](https://nodejs.org/dist/latest-v6.x/docs/api/vm.html)
講完這個知識點, 這里留下一個簡單的問題, 既然可以通過新的上下文來避免污染, 那么`為什么 Node.js 不給每一個 `.js` 文件以獨立的上下文來避免作用域被污染?` <del>(反應不過來的同學還是別投簡歷了, 微笑臉)</del>
## 包管理
整理中...
為什么我裝了全局, 但是提示我 not found
npm
yarn
鎖版本
lerna:一個用戶管理多個包模塊的工具。
left-pad事件
greenkeeper 等