## 模塊的分類
原生模塊(內置模塊、核心模塊)、文件模塊、第三方模塊
- 原生模塊:Node.js自帶的模塊
- 文件模塊:我們自己寫的模塊
- 第三方模塊:通過NPM下載的模塊
## 模塊的加載機制
下圖是個大概示例

## 文件的查找機制(非原生模塊查找)
查找文件時,它會進行如下幾種嘗試(按照從上到下的優先級)
### 先當做一個文件路徑名
- 嘗試直接查找該文件,
- 嘗試添加擴展名后查找文件
### 當做一個文件夾/包 路徑名
- 嘗試根據包查找文件
->從包文描述文件獲取文件名->嘗試添加擴展名后查找文件
- 嘗試查找該目錄下的index(.js/.node)
### 順著node_modules的鏈往上查找
如果到現在為止還沒找到,且加載的不是相對路徑,像這樣`requrie('./a')`,那么會繼續當做一個包并且順著`module.paths`的路徑依次往上查找node_modules里面有沒有這么一個包/文件夾
like this
```
// [ 'd:\\WEB\\A\\node-basic\\03module\\node_modules',
// 'd:\\WEB\\A\\node-basic\\node_modules',
// 'd:\\WEB\\A\\node_modules',
// 'd:\\WEB\\node_modules',
// 'd:\\node_modules' ]
```
## 模塊加載的簡單實現
**關鍵字:**`絕對路徑與resolvePathname`、`后綴名與不同的加載方式`、`module.exports`

首先我們定義一個我們自己的require方法和一個`Module`構造函數函數
```
function Module(){}
function req(filename){}
```
這個構造函數下有一些屬性
```
Module._cache = {} //用來存放已經加載的模塊
Module._extentions = ['.js','.json','.node']; //當文件沒有后綴名時候會從這里取
```
其中所存放的`cache`是長這樣的
```
//cache
{
"文件的絕對路徑":"Module的實例"
...
}
```
再讓我們看看rq方法,
其中要做的第一件事是解析傳入的文件名,是否是有效的,并且確保它是絕對路徑。
為什么要確保它是絕對路徑呢?有些相對路徑在某些編輯器環境中代表的真正路徑并不是我們想要的。我們使用絕對路徑來讀取文件會更可靠。
拿到解析后的路徑后我們會嘗試用這個路徑作為key去`Module._cache`中獲取緩存。
如有緩存是存在的我們就返回這個緩存模塊下的`exports`,如果不存在我們就加載這個模塊
```
function req(){
filename = Module._resolvePathname(filename);
let cacheModule = Module._cache['filename'];
if(cacheModule){
return cacheModule.exports;
}
let module = new Module(filename)
}
```
So,加載加載,是干了什么事呢?
我們所說的**加載**就是`Module`的實例化,并在這個實例上的`exports`上掛載我們`readFileSync`的那個文件內部`module.exports`所導出的內容。
但需要注意的是,加載不同的模塊時我們做的事其實是有所區別的,
```
Module.prototype.load = function(){
let ext = path.extname(this.filename);
Module._extentions[ext](this);
}
```
我們可以發現對于不同的文件類型我們將調用不同的方法去加載它。
- 如果是一個`json`文件,我們加載它就是直接將讀取到的內容掛載在`module.exports`上。
```
Module._extentions['json'] = function(module){
let json = fs.readFileSync(module.filename,'utf8');
json = JSON.parse(json);
return module.exports = json;
}
```
- 如果是一個`js`文件,我們加載它不僅要讀取它的內容還要讓這個js文件執行,并且是將它包起來作為一個閉包傳入我們的module和req讓它執行。
```
Module._extentions['js'] = function(module){
let script = fs.readFileSync(module.filename,'utf8');
script = Module._wrap(script);
vm.runInThisContext(script).call(module.exports,module.exports,req,module);
}
```
**注意:** 在js的加載中,我們是將實例化后的module傳入到閉包當中,在閉包中用module.exports導出模塊的內容的。
---
加載完成后,我們需要緩存模塊。
而緩存就是將`module`實例存儲到`Module._cahce`的過程,每一個`module`都有對應的一個`key`,這個key就是這個模塊的絕對路徑。
```
function req(){
...
Module._cache[filename] = module;
...
}
```

## 其它注意事項
- 模塊的加載**是同步的!**
## 源碼
倉庫地址:
[點我~](https://github.com/fancierpj0/module/blob/master/main.js)