Nodejs(模塊機(jī)制)

在Node 中引入模塊刨沦,需要經(jīng)歷三個(gè)步驟

  1. 路徑分析
  2. 文件定位
  3. 編譯執(zhí)行
    在Node中诗宣,模塊分為兩類:一類是Node提供的模塊,稱為核心模塊;一類是用戶編寫的模塊想诅,稱為文件模塊召庞。
  • 核心模塊部分在Node源碼的編譯過程中,編譯進(jìn)了二進(jìn)制執(zhí)行文件来破。在Node進(jìn)程啟動(dòng)時(shí)篮灼,部分核心模塊就被直接加載進(jìn)內(nèi)存中,所以這部分核心模塊引入時(shí)徘禁,文件定位和編譯執(zhí)行兩個(gè)步驟可以省略掉诅诱,并且在路徑分析中優(yōu)先判斷,所以它的加載速度是最快的送朱。

  • 文件模塊則是在運(yùn)行時(shí)動(dòng)態(tài)加載娘荡,需要完整的路徑分析、文件定位骤菠、編譯執(zhí)行過程它改,速度比核心模塊慢。

優(yōu)先從緩存加載

無論是核心模塊還是文件模塊商乎,require() 方法對(duì)相同模塊的二次加載都一律采用緩存優(yōu)先的方式央拖,不同之處在于核心模塊的緩存檢查優(yōu)先于文件模塊的緩存檢查。

路徑分析和文件定位

模塊標(biāo)識(shí)符分析

require()方法接受一個(gè)標(biāo)識(shí)符作為參數(shù)鹉戚。標(biāo)識(shí)符在Node中主要分為一下幾類:

  • 核心模塊鲜戒,例如http、fs抹凳、path等

  • .或..開始的相對(duì)路徑文件模塊

  • 以/開始的絕對(duì)路徑文件模塊

  • 非路徑形式的文件模塊,例如自定義模塊

核心模塊

核心模塊加載優(yōu)先級(jí)僅次于緩存赢底,它在Node源碼編譯過程中已經(jīng)編譯為二進(jìn)制代碼失都,其加載過程最快

路徑形式的文件模塊

在分析路徑模塊時(shí)柏蘑,require()方法會(huì)將路徑轉(zhuǎn)換為真實(shí)的路徑粹庞,并以真實(shí)路徑作為索引肖粮,將編譯執(zhí)行后的結(jié)果放在緩存中漫试,文件模塊給Node 指明了確切的文件位置,所以查找過程中可以節(jié)約大量時(shí)間商虐,其加載速度慢于核心模塊

自定義模塊

自定義模塊指的是非核心模塊典勇,也不是路徑形式的標(biāo)識(shí)符。它是一種特殊的文件模塊叮趴,可能是一個(gè)文件或者包的形式割笙,這類模塊是加載最慢的一種。

*** Node 定位文件模塊的查找策略*** 具體表現(xiàn)為一個(gè)路徑組成的數(shù)組眯亦∩烁龋可以手動(dòng)嘗試一下:

在任意目錄下執(zhí)行輸入node進(jìn)入node環(huán)境,然后輸入module.paths

在mac 下會(huì)得到下面一個(gè)數(shù)組輸出


> module.paths

[ '/Users/fanrongrong/repl/node_modules',

  '/Users/fanrongrong/node_modules',

  '/Users/node_modules',

  '/node_modules',

  '/Users/fanrongrong/.node_modules',

  '/Users/fanrongrong/.node_libraries',

  '/Users/fanrongrong/.nvm/versions/node/v8.9.3/lib/node' ]

>

模塊路徑的生成規(guī)則

  • 當(dāng)前文件目錄下的node_modules目錄

  • 父目錄下的node_modules目錄

  • 父目錄的父目錄下的node_modules目錄妻率,沿著路徑逐級(jí)遞歸乱顾,直到跟目錄

  • mac 會(huì)查找用戶模塊下的.node_modules和.node_libraries 目錄,window會(huì)查找環(huán)境變量$HOME下的這兩個(gè)目錄

  • node 的安裝目錄下的node_modules (全局安裝的包默認(rèn)在這里宫静,可以通過npm root -g查看路徑)

文件定位

文件擴(kuò)展名分析

require()在分析標(biāo)識(shí)符的過程中走净,允許在標(biāo)識(shí)符中不包含文件擴(kuò)展名,這種情況下Node會(huì)按照.js孤里、.json伏伯、.node的次序補(bǔ)足擴(kuò)展名

目錄分析和包

在分析標(biāo)識(shí)符的過程中,require()通過分析文件擴(kuò)展名之后捌袜,可能沒有查找到對(duì)應(yīng)的文件卻得到了一個(gè)目錄说搅,此時(shí)Node會(huì)將目錄當(dāng)作一個(gè)包來處理。

包處理規(guī)則:Node在當(dāng)前目錄查找package.json文件虏等,通過JSON.parse()解析出包的描述對(duì)象,從中取出main屬性指定的文件名進(jìn)行定位弄唧,如果文件名缺少擴(kuò)展名适肠,會(huì)進(jìn)入擴(kuò)展名分析步驟,如果main指定的文件名錯(cuò)誤候引,或沒有package.json文件迂猴,Node會(huì)將index當(dāng)作默認(rèn)文件名,然后依次查找index.js、index.json背伴、index.node,如果目錄分析中沒有定位到任何文件峰髓,在自定義模塊進(jìn)入下一個(gè)模塊的路徑分析傻寂,如果模塊的路徑數(shù)組都遍歷完依然沒有找的目標(biāo)文件,則會(huì)拋出查找失敗的異常携兵。

模塊編譯

去看源碼
Node會(huì)新建一個(gè)模塊對(duì)象疾掰,然后根據(jù)路徑載入并編譯,對(duì)于不同的文件擴(kuò)展名徐紧,載入的方式也不同静檬。

  • .js 文件。通過fs模塊同步讀取文件后編譯執(zhí)行
  • .node 文件并级。這是用c++編寫的擴(kuò)展文件拂檩,通過dlopen()方法加載最后編譯生成的文件
  • .json文件。通過fs模塊同步讀取文件后嘲碧,用JSON.parse()解析返回結(jié)果
  • 其余文件均當(dāng)作js文件引入

每一個(gè)編譯成功的模塊都會(huì)將其文件路徑作為索引緩存在Module._cache對(duì)象上稻励。
其中,Module._extensions 會(huì)被賦值給require()extensions屬性愈涩。通過console(require.extensions)可以查看已有的加載方式望抽;

JavaScript模塊的編譯

在編譯過程中,Node對(duì)獲取的JavaScript 文件內(nèi)容進(jìn)行包裝履婉,如下:

(function (exports, require, module,  __filename, __dirname) {
  var math = require('math')
  exports.area = function () {
    return Math.PI * radius * radius2
  }
})

包與NPM

包結(jié)構(gòu)

  • package.json: 包描述文件
  • bin : 存放可執(zhí)行二進(jìn)制文件目錄
  • lib: 存放JavaScript 代碼的目錄

包描述文件

  • main 模塊引入方法require方法會(huì)優(yōu)先檢查這個(gè)字段煤篙,并將它入口,如果不存在就查找index.js毁腿、index.node辑奈、index.json
  • bin 配置好bin 字段當(dāng)npm install 包名 -g 時(shí)可以將腳本添加到執(zhí)行路徑中

安裝依賴包

全局安裝模式
  • 根據(jù)bin字段的配置,將實(shí)際的腳本鏈接到與node 可執(zhí)行文件相同的路徑下:
"bin": {
  "express": "./bin/express"
}

通過全局安裝的模塊都會(huì)被安裝到一個(gè)統(tǒng)一目錄下狸棍,一搬都是node的安裝目錄下的lib/node_modules 下

node的包引用

node commonjs 規(guī)范引用包方式

node 在8.0版之前都是遵循commenjs 規(guī)范進(jìn)行的包引用(exports/module.exprots, require)
exports是module.exports 的一個(gè)引用身害,所以導(dǎo)出時(shí)可以使用exports.xxxx = xxx 的方式,而不能使用exports = xxxx的方式草戈,可以使用module.exports = xxx的方式
這種方式是運(yùn)行時(shí)加載塌鸯,換句話說是在 NodeJS 腳本執(zhí)行時(shí)才加載進(jìn)來

node 8.0 之后加入ES方式的引用包方式(import, export)

使用ES方式需要在啟動(dòng)node時(shí)加入?yún)?shù) --experimental-modules
這種方式引用是在靜態(tài)分析時(shí)候就確定了引用關(guān)系,就像目標(biāo)模塊建立了一個(gè)符號(hào)鏈接唐片,或者說建立了一個(gè)指針丙猬。這種加載方式加載效率應(yīng)該略高于 CommonJS涨颜。
例子:

// a.js
var n = 3;
exports.n = 3
exports.add = () => {
  n++;
}
// b.js
var mod = require('./a.js');
console.log(mod.n);
mod.add();
console.log(mod.n);
// a.mjs
export let n = 3;
export let add = () => {
  n++;
}
// b.mjs
import {
    n,
    add
} from './a.mjs'
console.log(n);
add();
console.log(n);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市茧球,隨后出現(xiàn)的幾起案子庭瑰,更是在濱河造成了極大的恐慌,老刑警劉巖抢埋,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弹灭,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡揪垄,警方通過查閱死者的電腦和手機(jī)穷吮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饥努,“玉大人捡鱼,你說我怎么就攤上這事】崂ⅲ” “怎么了驾诈?”我有些...
    開封第一講書人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)溶浴。 經(jīng)常有香客問我乍迄,道長(zhǎng),這世上最難降的妖魔是什么戳葵? 我笑而不...
    開封第一講書人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任就乓,我火速辦了婚禮,結(jié)果婚禮上拱烁,老公的妹妹穿的比我還像新娘生蚁。我一直安慰自己,他們只是感情好戏自,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開白布邦投。 她就那樣靜靜地躺著,像睡著了一般擅笔。 火紅的嫁衣襯著肌膚如雪志衣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,462評(píng)論 1 302
  • 那天猛们,我揣著相機(jī)與錄音念脯,去河邊找鬼。 笑死弯淘,一個(gè)胖子當(dāng)著我的面吹牛绿店,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼假勿,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼借嗽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起转培,我...
    開封第一講書人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤恶导,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后浸须,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惨寿,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年删窒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了缤沦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡易稠,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出包蓝,到底是詐尸還是另有隱情驶社,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布测萎,位于F島的核電站亡电,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏硅瞧。R本人自食惡果不足惜份乒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望腕唧。 院中可真熱鬧或辖,春花似錦、人聲如沸枣接。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽但惶。三九已至耳鸯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間膀曾,已是汗流浹背县爬。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留添谊,地道東北人财喳。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像碉钠,于是被迫代替她去往敵國(guó)和親纲缓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子卷拘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • nodejs 模塊機(jī)制 簡(jiǎn)單模塊定義和使用 在Node.js中,定義一個(gè)模塊十分方便祝高。我們以計(jì)算圓形的面積和周長(zhǎng)兩...
    艾倫先生閱讀 1,047評(píng)論 0 4
  • 說明:該學(xué)習(xí)筆記參考《深入淺出Node.js》在學(xué)習(xí)過程中栗弟,添加了自己的理解和適當(dāng)?shù)难a(bǔ)充!僅供參考工闺! NodeJs...
    秋意思寒閱讀 696評(píng)論 0 1
  • 之前 ECMAScript 的問題: 沒有模塊系統(tǒng)乍赫,標(biāo)準(zhǔn)庫較少(如文件系統(tǒng)等缺失API),沒有標(biāo)準(zhǔn)接口陆蟆,無包管理系...
    Air_cc閱讀 330評(píng)論 0 0
  • 早在Netscape誕生不久后雷厂,JavaScript就一直在探索本地編程的路,Rhino是其代表產(chǎn)物叠殷。無奈那時(shí)服務(wù)...
    itsmyturn閱讀 322評(píng)論 0 0
  • 1.為什么要CommonJS規(guī)范 javascript存在的缺點(diǎn) 沒有模塊系統(tǒng) 標(biāo)準(zhǔn)庫比較少 沒有標(biāo)準(zhǔn)接口 缺乏包...
    maikuraki閱讀 188評(píng)論 0 1