国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 網(wǎng)站 > WEB開發(fā) > 正文

CommonJS模塊規(guī)范與NodeJS的模塊系統(tǒng)底層原理

2024-04-27 15:17:32
字體:
供稿:網(wǎng)友

原諒我標(biāo)題黨 其實(shí)也沒有非常深入底層

在了解NodeJS模塊之前 首先來科普一下什么是CommonJS

CommonJS規(guī)范

它為javaScript制定一套規(guī)范——希望Javascript能在任何地方運(yùn)行 使其具備開發(fā)大型應(yīng)用的能力

出發(fā)點(diǎn)便是為了彌補(bǔ)當(dāng)時JavaScript語言自身的缺點(diǎn):

無模塊系統(tǒng) 現(xiàn)在ES6彌補(bǔ)了這個缺點(diǎn)沒有包管理胸痛 導(dǎo)致js應(yīng)用沒有自加載和安裝依賴能力無標(biāo)準(zhǔn)接口 沒有定義過像Web服務(wù)器一類的標(biāo)準(zhǔn)統(tǒng)一接口標(biāo)準(zhǔn)庫太少 僅有部分核心庫,文件系統(tǒng)等常見需求沒有標(biāo)準(zhǔn)API;H5推進(jìn)了這個過程,但也只是瀏覽器端

CommonJS-API寫出的應(yīng)用可以跨宿主環(huán)境 這樣JavaScript就不僅僅只是停留在客戶端,他還可以開發(fā):

服務(wù)器端JS應(yīng)用命令行工具桌面圖形界面應(yīng)用程序混合應(yīng)用(原生應(yīng)用內(nèi)嵌瀏覽器)

CommonJS涵蓋一下內(nèi)容

模塊I/O流二進(jìn)制緩沖區(qū)套接字進(jìn)程環(huán)境文件系統(tǒng)單元測試字符集編碼Web服務(wù)器網(wǎng)關(guān)接口包管理……

CommonJS模塊

為什么要介紹CommonJS 因?yàn)镹ode簡單易用的模塊系統(tǒng)就是借鑒了CommonJS的Modules規(guī)范 CommonJS模塊分為三部分:模塊引用、模塊定義、模塊標(biāo)識

模塊導(dǎo)出

Node中,一個文件就是一個模塊 模塊中使用exports導(dǎo)出當(dāng)前模塊的變量或函數(shù) 下面我就建立一個tool.js的工具模塊 并且通過exports導(dǎo)出

//tool.jsvar add = function add(a, b){ return a + b;}exPRots.add = add;

導(dǎo)出給外部的對象 就擁有一個add方法

不過要特別注意 如果直接給exports賦值為一個基本類型值不會成功 比如我們只是想單純的導(dǎo)出一個數(shù)字(雖然不會這么用)

exports = 123;

是完全不能夠?qū)С龅?至于為什么下面再說


我們一般不會直接通過exports這么用 在我們模塊的上下文中,還有一個module對象,引用我們模塊自身 而這個exports對象便是module上的屬性 我們通常的做法就是通過 module.exprots 導(dǎo)出

//tool.jsvar tool = { add: function (a, b){ return a + b; }}module.exprots = tool;

模塊引用

模塊引用很簡單 只需調(diào)用require()方法,接收一個模塊標(biāo)識字符串作為參數(shù) 如此引入一個模塊到我們當(dāng)前的環(huán)境中

var tool = require('./tool');

我們調(diào)用起來倒是輕松愉快 其實(shí)內(nèi)部發(fā)生了日異月殊的變化(下面再說)

引入之后,我們就能調(diào)用內(nèi)部的API了

tool.add(1, 2); //3

模塊標(biāo)識

模塊標(biāo)識就是我們傳遞給require()的那個字符串參數(shù) 這個字符串是符合小駝峰命名的字符串 或者是 . / .. 開頭的相對路徑,再或者絕對路徑 如果要引入的模塊后綴為 .js / .json / .node 可以省略

這種模塊機(jī)制導(dǎo)入容易,導(dǎo)出也容易 把類聚的方法和變量限定在私有作用域內(nèi) 模塊之間空間獨(dú)立、互不干擾,好處不言而喻 媽媽再也不用擔(dān)心我們變量污染了

NodeJS模塊原理

NodeJS在CommonJS模塊規(guī)范基礎(chǔ)上作出了改動 在了解NodeJS模塊原理之前 先來了解一下NodeJS的模塊緩存機(jī)制

模塊緩存機(jī)制

NodeJS為了提高性能,我們引入模塊后,它都會進(jìn)行緩存 這和我們在瀏覽器端的很像 但是瀏覽器緩存的是文件 而Node緩存編譯執(zhí)行后的對象 我們可以做一個實(shí)驗(yàn)

//increase.jsvar a = 0;var increase = function(){ ++a;}//index.jsvar increase = require('./increase');console.log(increase());console.log(increase());var add = require('./tool');console.log(increase());console.log(increase());

實(shí)驗(yàn)的結(jié)果返回了 1 2 3 4 而不是 1 2 1 2 這就證明二次引用時實(shí)際引用了緩存的對象(編譯執(zhí)行后的模塊)

所以當(dāng)我們調(diào)用require( )方法時 Node會優(yōu)先查看緩存(第一優(yōu)先級),沒有緩存再進(jìn)行一系列過程 這一系列過程就是:

路徑分析文件定位編譯執(zhí)行

了解這些過程前 我們還要知道模塊分類

模塊種類

模塊大體上分兩種,它們還可以細(xì)分

核心模塊:Node提供的模塊 JavaScript核心模塊C/C++核心模塊文件模塊:用戶編寫的模塊 本地模塊:本地編寫模塊第三方模塊:從第三方下載的模塊

核心模塊在Node源碼編譯過程中,編譯進(jìn)二進(jìn)制執(zhí)行文件 Node啟動,部分核心模塊被直接加載進(jìn)內(nèi)存 所以文件定位和編譯執(zhí)行階段可省略,并且優(yōu)先判斷路徑分析(加載最快)

文件模塊運(yùn)行時動態(tài)加載,速度稍慢

模塊引入原理

路徑分析

路徑分析的優(yōu)先級如下:

緩存加載核心模塊加載文件模塊加載

如果引入的是核心模塊,就直接填寫模塊名字符串就可以了

var http = require('http');

如果引入的是文件模塊,就會根據(jù)填入的路徑來定位文件

var tool = require('./tool');

我們下載的第三方模塊會存在于node_modules的文件夾 在分析它的時候 就會查找當(dāng)前目錄下的node_modules中有沒有該文件 如果沒找到,就會查找父級目錄下有沒有node_modules并查找 以此類推 引用第三方模塊同樣不必輸入路徑

var react = require('react');

文件定位

require()分析標(biāo)識符的時候,可能會出現(xiàn)省略文件擴(kuò)展名的情況 此時,Node會按照 .js / .json / .node 的順序依次嘗試 很顯然這有一點(diǎn)兒性能問題,嘗試也需要時間 所以我們最好給 .json.node 形式的文件添加擴(kuò)展名

如果我們定位到的是一個文件夾 Node會把它當(dāng)做一個包來處理 根據(jù)包內(nèi)部的package.json文件的main屬性繼續(xù)定位入口文件

關(guān)于包的概念,這里不講 可以暫時把它理解為擁有package.json配置文件的一個文件夾

模塊編譯

文件格式不同,載入方法也不同

.js文件:通過fs核心模塊同步讀取后編譯執(zhí)行.node文件:C/C++擴(kuò)展文件,通過dlopen()加載最后編譯生成的文件.json文件:通過fs核心模塊同步讀取后利用JSON.parse()解析其他:均當(dāng)做.js文件處理

這里我只說一下JavaScript文件模塊的編譯 大家一定很奇怪一個問題 我們的文件中根本沒有什么exports,沒有什么require,它們從哪兒來的? 答案就在這里 就拿我們上面的模塊為例

//increase.jsvar a = 0;var increase = function(){ ++a;}

在這個編譯過程中,Node實(shí)際上對JS文件進(jìn)行了包裝 加上了“龍頭鳳尾”(致敬兒時玩的四驅(qū)車) 龍頭:(function(exports, require, module, __filename, __dirname){/n 鳳尾:/n}); 封裝后的文件變成了這樣

(function(exports, require, module, __filename, __dirname){ var a = 0; var increase = function(){ ++a; }});

這回我們就可以理解為什么直接給exports賦基本類型值不可以 因?yàn)閑xports實(shí)際上作為形參傳入 賦值僅僅只是改變了形參


包裝后的代碼通過原生vm模塊的runInThisContext()執(zhí)行(類似eval) 返回一個函數(shù) 最后將當(dāng)前模塊的exports屬性、require方法等等傳入這個函數(shù)執(zhí)行

==主頁傳送門==


發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 津市市| 盐城市| 桐城市| 东乡族自治县| 本溪| 呼图壁县| 芒康县| 平江县| 磐安县| 改则县| 如东县| 辽中县| 庆元县| 长丰县| 彭水| 广汉市| 化州市| 蛟河市| 沂水县| 安阳县| 武乡县| 祥云县| 泰顺县| 宁南县| 米泉市| 双鸭山市| 酒泉市| 正蓝旗| 柳林县| 望奎县| 潜山县| 江达县| 阜阳市| 海伦市| 仁怀市| 常州市| 清丰县| 那坡县| 双柏县| 贵德县| 多伦县|