nodejs require執行流程

來源:互聯網
上載者:User

標籤:傳遞   while   []   全域   目錄   最佳化   tor   r.js   javascrip   

為什麼要加這個必讀!因為webpack本身是基於node環境的,

裡面會涉及很多重路徑問題,我們可能對paths怎麼寫!webpack又是怎麼找到這些paths的很迷惑。

本文是我已經寫完正式學習React(五)後新加的!我覺得很有必要讓大家知道!  

------------------------------------------------------------------------------------------------------

nodejs require執行流程  ==== webpack的路徑尋找過程!

 

-----------------------------------------------------------------------------------------------------

require最常用的方法
require(‘http‘) 內建模組

require(‘./server‘)  “./”表示當前路徑,後面跟的是相對路徑
require("../lib/server") ../表示上一級目錄,後面跟的也是相對路徑


server.js

[javascript] 
var http = require(‘http‘); 
function start(){ 
    server = http.createServer(function (req, res) {   
          res.writeHeader(200, {"Content-Type": "text/plain"});   
          res.end("Hello oschina\n");   
    })   
    server.listen(8000);   
    console.log("httpd start @8000");  

exports.start = start;  

index.js

[javascript] 
//路徑根據自己的實際情況而定 
var server = require("./learnNode/server"); 
server.start(); 

下面介紹require的只是來自於連結:http://www.nodecn.org/modules.html#file_Modules

模組
Node 使用 CommonJS 模組系統。

Node 有一個簡單的模組載入系統。在 Node 中,檔案和模組一一對應。比如,在 foo.js 載入同一目錄中的circle.js 模組。

foo.js 的內容:

var circle = require(‘./circle.js‘);
console.log( ‘The area of a circle of radius 4 is ‘
           + circle.area(4));
circle.js 的內容:

var PI = Math.PI;

exports.area = function (r) {
  return PI * r * r;
};

exports.circumference = function (r) {
  return 2 * PI * r;
};
模組 circle.js 匯出了 area() 函數和 circumference() 函數,這樣它們就能從模組外部存取了。要匯出對象,將其添加到特殊的 exports 對象就行。

模組的局部變數是私人的。在本例中,變數 PI 是 circle.js 私人的。

核心模組
Node 有一些已編譯成二進位的模組,這些模組將在本文檔的其他地方詳細??介紹。

核心模組在 Node 原始碼的 lib/ 檔案夾中定義。

使用 require() 時,核心模組總是優先載入。例如,require(‘http‘) 總是返回內建的 HTTP 模組,即使該名稱的檔案存在。

檔案模組
如果沒有找到確切的檔案,Node 將嘗試給所需的檔案名稱??添加 .js 尾碼再載入,然後再嘗試 .node。

.js 檔案被視為 JavaScript 文字檔,而 .node 檔案被視為已編譯的外掛程式模組,用 dlopen 載入。

模組以 ‘/‘ 開頭表示使用檔案的絕對路徑。例如,require(‘/home/marco/foo.js‘) 將載入/home/marco/foo.js 檔案。

模組以 ‘./‘ 開頭表示調用 require() 時使用相對路徑。也就是說,為了保證 require(‘./circle‘) 能找到,circle.js 必須和 foo.js 在同一目錄。

如果不以 ‘/‘ 或‘./‘ 開頭,該模組可以是一個“核心模組”,也可是一個從 node_modules 檔案夾中載入的模組。

從 `node_modules` 檔案夾中載入
如果傳遞給 require() 有模組標識符是不是原生模組,而且不以 ‘/‘、‘../‘ 或‘./‘ 開頭,那麼 Node 從當前模組的父目錄+/node_modules 這個位置嘗試載入。

如果還是沒有找到,那麼它跳到上層目錄並依此類推,直到找到模組,或者達到根目錄為止。

例如,如果在檔案 ‘/home/ry/projects/foo.js‘ 中調用 require(‘bar.js‘),那麼 Node 將在下列位置尋找,順序如下:

/home/ry/projects/node_modules/bar.js
/home/ry/node_modules/bar.js
/home/node_modules/bar.js
/node_modules/bar.js
這就允許程式將依賴關係本地化,防止它們衝突。

最佳化 `node_modules` 尋找過程

當嵌套依賴關係的層次很深時,這個檔案尋找列表可能會變得很長。因此,在尋找時進行如下最佳化:

首先,/node_modules 不會附加到一個以 /node_modules 結尾的檔案夾後面。

其次,如果調用 require() 的檔案已經在一個 node_modules 層級裡,那麼最頂層的 node_modules 檔案夾將被視為搜尋樹的根。

例如,如果在檔案 ‘/home/ry/projects/foo/node_modules/bar/node_modules/baz/quux.js‘ 中調用require(‘asdf.js‘),那麼 Node 將搜尋下列位置:

/home/ry/projects/foo/node_modules/bar/node_modules/baz/node_modules/asdf.js
/home/ry/projects/foo/node_modules/bar/node_modules/asdf.js
/home/ry/projects/foo/node_modules/asdf.js
以檔案夾作為模組
Node 允許使用者在獨立的目錄中方便地組織程式,然後提供單一入口指向該庫。有三種方式可以將檔案夾作為require() 的參數。

第一種方式是在該檔案夾中建立 package.json 檔案,指定一個 main 模組。一個典型的 package.json 檔案可能看起來像這樣:

{ "name" : "some-library",
  "main" : "./lib/some-library.js" }
如果此檔案位於 ./some-library 檔案夾,則 require(‘./some-library‘) 會嘗試載入 ./some-library/lib/some-library.js。

這是 Node 能找到 package.json 檔案的情況。

如果在該目錄中沒有 package.json 檔案,那麼 Node 將嘗試載入該目錄中的 index.js 或 index.node 檔案。例如,如果上面的例子找不到 package.json,那麼 require(‘./some-library‘) 將試圖載入:

./some-library/index.js
./some-library/index.node
緩衝
模組在首次被載入後會緩衝起來。這意味著每次調用 require(‘foo‘) 將得到完全相同的對象,如果它被解析為同一個檔案的話。

總結……
為了得到調用 require() 時被載入的確切的檔案名稱,使用 require.resolve() 函數。

綜上所述,這是 require.resolve 的偽碼描述:

require(X)
1. If X is a core module,
   a. return the core module
   b. STOP
2. If X begins with `./` or `/`,
   a. LOAD_AS_FILE(Y + X)
   b. LOAD_AS_DIRECTORY(Y + X)
3. LOAD_NODE_MODULES(X, dirname(Y))
4. THROW "not found"

LOAD_AS_FILE(X)
1. If X is a file, load X as JavaScript text.  STOP
2. If X.js is a file, load X.js as JavaScript text.  STOP
3. If X.node is a file, load X.node as binary addon.  STOP

LOAD_AS_DIRECTORY(X)
1. If X/package.json is a file,
   a. Parse X/package.json, and look for "main" field.
   b. let M = X + (json main field)
   c. LOAD_AS_FILE(M)
2. LOAD_AS_FILE(X/index)

LOAD_NODE_MODULES(X, START)
1. let DIRS=NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
   a. LOAD_AS_FILE(DIR/X)
   b. LOAD_AS_DIRECTORY(DIR/X)

NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let ROOT = index of first instance of "node_modules" in PARTS, or 0
3. let I = count of PARTS - 1
4. let DIRS = []
5. while I > ROOT,
   a. if PARTS[I] = "node_modules" CONTINUE
   c. DIR = path join(PARTS[0 .. I] + "node_modules")
   b. DIRS = DIRS + DIR
6. return DIRS
從 `require.paths` 載入
在 Node 中,require.paths 是一個字串數組,表示模組不以 ‘/‘ ‘./‘ 或 ‘..‘ 打頭的搜尋路徑。例如,如果 require.paths 設定為:

[ ‘/home/micheil/.node_modules‘,
  ‘/usr/local/lib/node_modules‘ ]
則調用 require(‘bar/baz.js‘) 會搜尋以下位置:

1: ‘/home/micheil/.node_modules/bar/baz.js‘
2: ‘/usr/local/lib/node_modules/bar/baz.js‘
可以在運行時修改 require.paths 數組來改變這種行為。

它的值最初從 NODE_PATH 環境變數而來,那是一個冒號分隔的絕對路徑列表。在前面的例子中,NODE_PATH 環境變數可能被設定為:

/home/micheil/.node_modules:/usr/local/lib/node_modules
只有使用上面的 node_modules 演算法找不到模組時才會嘗試 require.paths。全域模組的優先順序低於捆綁依賴。

**注意** 請不要修改 `require.paths`

出於相容性的考慮,require.paths 仍然是模組尋找過程的首選策略。儘管如此,它可能會在將來的版本中廢棄。

雖然它看起來似乎是個好主意,但在實踐中一個可變的 require.paths 列表往往是麻煩和混亂的根源。

修改 `require.paths` 毫無用處

這行代碼並不會像期望的那樣:

require.paths = [ ‘/usr/lib/node‘ ];
它的結果就是丟棄了 Node 實際的模組尋找路徑引用,並建立了一個毫無用處的指向別處的新的引用。

在 `require.paths` 中加入相對路徑……不是個好主意

如果你這樣做:

require.paths.push(‘./lib‘);
它不會添加 ./lib 在檔案系統上已解析的完整路徑。相反,它實際增加的是 ‘./lib‘,這意味著如果你在/a/b/x.js 中 require(‘y.js‘),那麼它會尋找 /a/b/lib/y.js。如果你之後又在 /l/m/n/o/p.js 中require(‘y.js‘),那麼它就會尋找 /l/m/n/o/lib/y.js。

在實踐中,人們往往將它作為捆綁依賴的臨時解決辦法,這個技巧是不太穩妥的。

零隔離

有一種糟糕的設計:所有模組共用一個 require.paths 數組。

結果,如果一個 Node 程式依賴於這種行為,它可能會永久而微妙地改變同一進程中其它 Node 程式的行為。當應用程式的複雜度增加時,我們傾向於封裝功能,這些行為很難預料的部分會成為開發人員的惡夢。

增編:軟體包管理小貼示
在 Node 中,require() 函數的語義被設計成通用性足以支援大量合理的目錄結構。因此 dpkg、rpm 和 npm之類的包管理器可以從 Node 模組構建原生包而不作更改。

下面我們給出一個可以工作的建議的目錄結構:

比方說,我們希望 /usr/lib/node/<some-package>/<some-version> 檔案夾中包含某個包的特定版本的內容。

一個軟體包可以依賴別的包。為了安裝 foo 包,你可能需要安裝 bar 包的特定版本 。可能該 bar 包本身有依賴關係,在某些情況下,這些依賴關係甚至可能發生衝突或者形成迴路。

由於 Node 在載入任何模組時都會尋找它的真實路徑(即:會解析符號連結),然後在 node_modules 檔案夾用上文描述的方式尋找依賴。使用以下架構可以很簡單地解決:

/usr/lib/node/foo/1.2.3/ -foo 包的內容,版本1.2.3。
/usr/lib/node/bar/4.3.2/ -bar 包的內容,foo 依賴這個包。
/usr/lib/node/foo/1.2.3/node_modules/bar -到 /usr/lib/node/bar/4.3.2/ 的符號連結。
/usr/lib/node/bar/4.3.2/node_modules/* -到 bar 所依賴的包的符號連結。
因此,即使遇到一個迴路,或者有依賴衝突,每個模組都能夠得到它依賴的可用版本。

當 foo 包中有代碼 require(‘bar‘) 時,它會得到符號連結至/usr/lib/node/foo/1.2.3/node_modules/bar 的版本。然後,當 bar 包調用 require(‘quux‘) 時,它會得到符號連結至 /usr/lib/node/bar/4.3.2/node_modules/quux 的版本。

此外,為了使模組尋找過程更加最佳化,而??不是直接把包放到 /usr/lib/node 中,我們可以它們放到/usr/lib/node_modules/<name>/<version> 裡。這樣,Node 就不用在 /usr/node_modules 或/node_modules 中尋找了。

為了使 REPL 能夠正常引用模組,可以將 /usr/lib/node_modules 添加至 $NODE_PATH環境變數。因為使用node_modules 檔案夾尋找模組時的路徑都是相對的,而且調用 require() 時基於檔案的真實路徑,因此軟體包本身可以放在任何位置。

nodejs require執行流程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.