標籤:傳遞 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執行流程