成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

node模塊加載層級(jí)優(yōu)化

eccozhou / 3278人閱讀

摘要:環(huán)境變量法通過(guò)上一節(jié)的源碼分析,我們知道了的作用,那么如何使用或者優(yōu)雅的使用來(lái)解決依賴(lài)加載問(wèn)題呢嘗試一最為直接的是,修改系統(tǒng)的環(huán)境變量。

模塊加載痛點(diǎn)

大家也或多或少的了解node模塊的加載機(jī)制,最為粗淺的表述就是依次從當(dāng)前目錄向上級(jí)查詢(xún)node_modules目錄,若發(fā)現(xiàn)依賴(lài)則加載。但是隨著應(yīng)用規(guī)模的加大,目錄層級(jí)越來(lái)越深,若是在某個(gè)模塊中想要通過(guò)require方式以依賴(lài)名稱(chēng)或相對(duì)路徑的方式引用其他模塊就非常麻煩,影響開(kāi)發(fā)效率和美觀(guān)。

示例demo:

// 當(dāng)前目錄: /usr/local/test/index.js
// gulp模塊所在路徑為 /usr/lib/node_modules

var gulp = require("../../lib/gulp");
gulp.task("say",function(){
    console.log("hello wolrd");
});

目前的條件下,只有采用上述中相對(duì)路徑的方式引用依賴(lài)模塊,可以看出上述引用的缺點(diǎn):

丑陋,十分繁雜

容易出錯(cuò),難以維護(hù)

第二個(gè)缺點(diǎn)是最難以接受的,在多次引用模塊的情況下問(wèn)題會(huì)被放大,因此急需尋找某種方案解決多層目錄依賴(lài)引用,本文將會(huì)討論筆者在開(kāi)發(fā)過(guò)程中的一些嘗試,并歡迎大家一起討論其他可行性方案。

全局變量法

由于目標(biāo)是解決毫無(wú)美觀(guān)又難以理解的相對(duì)目錄層級(jí),那么可以嘗試使用變量完成目錄層級(jí)的替代。這種方案最為直接,且node加載該依賴(lài)的速度最快,無(wú)需遍歷其他各級(jí)目錄。但是為了更為通用,筆者常采用全局變量的方式綁定目錄關(guān)系:

demo:

// 當(dāng)前目錄: /usr/local/test/index.js
// gulp模塊所在路徑為 /usr/lib/node_modules

global._root = "/usr/lib/node_modules";
var path = require("path");
var gulp = require(path.join(_root,"gulp"));
...

這種方案最為直接,但是可擴(kuò)展性并不強(qiáng),而且在多人維護(hù)的情況下尤甚,因此建議在單人開(kāi)發(fā)的小項(xiàng)目中采用。

直接引用模塊名

直接引用模塊名,說(shuō)到底就是直接引用node_modules目錄中的依賴(lài),類(lèi)似引用node默認(rèn)加載的那些模塊,如http,event模塊。

demo:

// 當(dāng)前目錄: /usr/local/test/index.js
// gulp模塊所在路徑為 /usr/lib/node_modules

var gulp = require("gulp");
...

在目錄/usr/local/test、/usr/local、/usr、/四個(gè)目錄下都沒(méi)有“node_modules”目錄或者“node_modules”目錄下都沒(méi)有g(shù)ulp模塊,那么運(yùn)行這個(gè)文件,肯定會(huì)報(bào)錯(cuò)“MODULE_NOT_FOUND”,這就是我們接下來(lái)需要解決的問(wèn)題,即如何修改node加載依賴(lài)的層級(jí)關(guān)系。

修改依賴(lài)加載層級(jí)

相信大家學(xué)習(xí)node也都讀過(guò)一本書(shū)《深入淺出nodejs》,這本書(shū)的第二章第二節(jié)曾簡(jiǎn)要介紹node加載依賴(lài)所遍歷的一些目錄,書(shū)中讓我們?cè)谀硞€(gè)測(cè)試文件中輸出module.paths,結(jié)果是一個(gè)數(shù)組,類(lèi)似于

["/usr/local/test/node_modules"、"/usr/local/node_modules"、"/usr/node_modules"、"/node_modules"]

這給我們一個(gè)啟發(fā),即加載某個(gè)模塊的順序就是按照上述數(shù)組項(xiàng)的順序依次判斷模塊是否存在,若存在則加載,事實(shí)上node也確實(shí)是這樣做的(下文會(huì)針對(duì)源碼分析猜想的正確性)。那么,在猜想的基礎(chǔ)上我們可以嘗試修改該數(shù)組下可否影響本模塊加載依賴(lài)的順序,如果成功自然美麗,如若不成功需尋找更為恰當(dāng)?shù)慕鉀Q方案。

嘗試1:

// 當(dāng)前目錄: /usr/local/test/index.js
// gulp模塊所在路徑為 /usr/lib/node_modules

module.paths.push("/usr/lib/node_modules");
console.log(module.paths);
var gulp = require("gulp");

執(zhí)行命令,一切正常,成功了。通過(guò)輸出信息可看出

["/usr/local/test/node_modules"、"/usr/local/node_modules"、"/usr/node_modules"、"/node_modules","/usr/lib/node_modules"]

確實(shí)修改了依賴(lài)查找層級(jí),不過(guò)可以看出設(shè)置的目錄是在數(shù)組中的最后一位,這意味著node會(huì)在找到gulp依賴(lài)前遍歷4層目錄,最后才在第五層目錄中找到它。如果項(xiàng)目中只引用了gulp也還好,但是隨著其他依賴(lài)的數(shù)量增多,運(yùn)行時(shí)加載依賴(lài)/usr/lib/node_modules下的依賴(lài)將會(huì)耗費(fèi)不少時(shí)間。因此建議大家在項(xiàng)目中評(píng)估好依賴(lài)的位置,如果合適的話(huà)可以?xún)?yōu)先加載手動(dòng)設(shè)置的依賴(lài)目錄:

// 當(dāng)前目錄: /usr/local/test/index.js
// gulp模塊所在路徑為 /usr/lib/node_modules

module.paths.unshift("/usr/lib/node_modules");
console.log(module.paths);
var gulp = require("gulp");

這樣,我們?cè)诓恢纍ode底層如何工作的前提下就實(shí)現(xiàn)了目標(biāo)。哈哈,不過(guò)作為一名靠譜的前端(node)工程師,我們不會(huì)滿(mǎn)足這種程度吧?哈哈!

深入源碼探究

筆者摘出了與模塊(依賴(lài))加載相關(guān)的代碼:

// 初始化全局的依賴(lài)加載路徑
Module._initPaths = function() {
  ...
  var paths = [path.resolve(process.execPath, "..", "..", "lib", "node")];

  if (homeDir) {
    paths.unshift(path.resolve(homeDir, ".node_libraries"));
    paths.unshift(path.resolve(homeDir, ".node_modules"));
  }

  // 我們需要著重關(guān)注此處,獲取環(huán)境變量“NODE_PATH”
  var nodePath = process.env["NODE_PATH"];
  if (nodePath) {
    paths = nodePath.split(path.delimiter).concat(paths);
  }

  // modulePaths記錄了全局加載依賴(lài)的根目錄,在Module._resolveLookupPaths中有使用
  modulePaths = paths;

  // clone as a read-only copy, for introspection.
  Module.globalPaths = modulePaths.slice(0);
};

// @params: request為加載的模塊名 
// @params: parent為當(dāng)前模塊(即加載依賴(lài)的模塊)
Module._resolveLookupPaths = function(request, parent) {
  ...
 
  var start = request.substring(0, 2);
  // 若為引用模塊名的方式,即require("gulp")
  if (start !== "./" && start !== "..") {
    // 此處的modulePaths即為Module._initPaths函數(shù)中賦值的變量
    var paths = modulePaths;
    if (parent) {
      if (!parent.paths) parent.paths = [];
      paths = parent.paths.concat(paths);
    }
    return [request, paths];
  }

  // 使用eval執(zhí)行可執(zhí)行字符串的情況下,parent.id 和parent.filename為空
  if (!parent || !parent.id || !parent.filename) {
    var mainPaths = ["."].concat(modulePaths);
    mainPaths = Module._nodeModulePaths(".").concat(mainPaths);
    return [request, mainPaths];
  }
  
  ...
};

Module._initPaths函數(shù)在默認(rèn)的生命周期內(nèi)只執(zhí)行一次,作用自然是設(shè)置全局加載依賴(lài)的相對(duì)路徑。而當(dāng)每次在文件中執(zhí)行require加載其他依賴(lài)時(shí),Module._resolveLookupPaths函數(shù)都會(huì)執(zhí)行,返回一個(gè)包含依賴(lài)名和可遍歷的目錄數(shù)組(該數(shù)組中的目錄項(xiàng)可以加載到依賴(lài),也可以無(wú)法加載依賴(lài))。最后的工作就是根據(jù)Module._resolveLookupPaths函數(shù)返回的結(jié)果,遍歷目錄數(shù)組,加載依賴(lài)。如果遍歷結(jié)束后仍沒(méi)有找到依賴(lài),則拋錯(cuò)。

在分析完源碼后,相信大家也都注意了幾點(diǎn)信息:

Module._initPaths函數(shù)內(nèi)部檢查了NODE_PATH環(huán)境變量

Module._initPaths函數(shù)只執(zhí)行一次

Module._initPaths函數(shù)初始化的全局依賴(lài)加載路徑與module.paths有關(guān)系

那么,我們可以從另一個(gè)角度解決依賴(lài)加載的問(wèn)題。

環(huán)境變量法

通過(guò)上一節(jié)的源碼分析,我們知道了NODE_PATH的作用,那么如何使用或者優(yōu)雅的使用NODE_PATH來(lái)解決依賴(lài)加載問(wèn)題呢?

嘗試一

最為直接的是,修改系統(tǒng)的環(huán)境變量。在linux下,執(zhí)行

export NODE_PATH=/usr/lib/node_modules

即可解決。

但是,這種方案畢竟不優(yōu)雅,因?yàn)槲覀兊囊粋€(gè)項(xiàng)目就修改了系統(tǒng)的環(huán)境變量,如果其他項(xiàng)目也采用這種方案,那么相信系統(tǒng)的NODE_PATH將會(huì)變得很長(zhǎng),而且會(huì)由于NODE_PATH的子路徑順序問(wèn)題出現(xiàn)意想不到的沖突,因此作為這種解決方案不建議使用。

嘗試二

我們希望只針對(duì)當(dāng)前運(yùn)行的程序設(shè)置環(huán)境變量,不影響其他程序;而且一旦當(dāng)前程序退出,設(shè)置的環(huán)境變量也被恢復(fù)。滿(mǎn)足這種需求的實(shí)現(xiàn),最為直觀(guān)的就是命令行配置。通過(guò)查閱node手冊(cè)可以這樣運(yùn)行:

NODE_PATH=/usr/lib/node_modules  node /usr/local/test/index.js

這樣,仍可以成功加載gulp依賴(lài),而不影響系統(tǒng)的環(huán)境變量。

但是,命令行的方式顯而易見(jiàn),就是丑陋,麻煩。每次運(yùn)行程序都需要提前輸入一系列的路徑,這種方式將代碼的可維護(hù)性變?yōu)榱顺绦虻目删S護(hù)性,在負(fù)責(zé)的項(xiàng)目中不適合使用。

嘗試三

node運(yùn)行時(shí)給我們提供了一個(gè)變量,對(duì),就是process。process是node默認(rèn)加載的Process模塊的一個(gè)屬性,通過(guò)process可獲取應(yīng)用進(jìn)程的相關(guān)信息,同時(shí)包括設(shè)置的環(huán)境變量。

我們可以在應(yīng)用的入口文件設(shè)置環(huán)境變量:

// 當(dāng)前目錄: /usr/local/test/index.js
// gulp模塊所在路徑為 /usr/lib/node_modules
process.env.NODE_PATH="/usr/lib/node_modules";
var gulp = require("gulp");

這樣我們?cè)趫?zhí)行文件,意想不到的事情發(fā)生了,仍報(bào)出“MODULE_NOT_FOUND”錯(cuò)誤。

這是為什么呢?原因仍要追溯到源碼。在源碼分析小節(jié)中總結(jié)了三點(diǎn),其中第二點(diǎn)提到了Module._initPaths函數(shù)只執(zhí)行一次,這意味著當(dāng)我們?cè)诖a中設(shè)置了process.env.NODE_PATH="/usr/lib/node_modules";,可是由于此時(shí)Module._initPaths已執(zhí)行完畢,因此設(shè)置的環(huán)境變量并沒(méi)有被使用。解決這個(gè)問(wèn)題也比較簡(jiǎn)單,即重新調(diào)用Module._initPaths即可。

// 當(dāng)前目錄: /usr/local/test/index.js
// gulp模塊所在路徑為 /usr/lib/node_modules
process.env.NODE_PATH="/usr/lib/node_modules";
require("module").Module._initPaths();
// 或者 module.constructor._initPaths()
var gulp = require("gulp");

這樣,安全無(wú)公害的解決了多基目錄下依賴(lài)調(diào)用的問(wèn)題。

總結(jié)

本文從實(shí)際開(kāi)發(fā)中遇到的問(wèn)題出發(fā),提出了幾種解決多基目錄下依賴(lài)的幾種方案:

全局變量法

修改module.paths方法

環(huán)境變量法(三種實(shí)現(xiàn))

當(dāng)然,社區(qū)還有一些幫助解決這種問(wèn)題的模塊,如“app-module-path”,但思想也大同小異。在這里和大家一起分享學(xué)習(xí)收獲,希望對(duì)各位有些啟發(fā)和感悟,不勝感激!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/80932.html

相關(guān)文章

  • Node】前后端模塊規(guī)范與模塊加載原理

    摘要:例如指定一些依賴(lài)到模塊中實(shí)現(xiàn)規(guī)范的模塊化,感興趣的可以查看的文檔。 CommonJS 定義了 module、exports 和 require 模塊規(guī)范,Node.js 為了實(shí)現(xiàn)這個(gè)簡(jiǎn)單的標(biāo)準(zhǔn),從底層 C/C++ 內(nèi)建模塊到 JavaScript 核心模塊,從路徑分析、文件定位到編譯執(zhí)行,經(jīng)歷了一系列復(fù)雜的過(guò)程。簡(jiǎn)單的了解 Node 模塊的原理,有利于我們重新認(rèn)識(shí)基于 Node 搭建的...

    jsyzchen 評(píng)論0 收藏0
  • Webpack 4.0 CommonsChunkPlugin 和 optimization spli

    摘要:可以通過(guò)配置屬性進(jìn)行修改,默認(rèn)將會(huì)自動(dòng)創(chuàng)建個(gè)庫(kù)文件僅含有依然會(huì)創(chuàng)建個(gè)庫(kù)文件僅含有假設(shè)所有的體積都大于將會(huì)創(chuàng)建一個(gè)庫(kù)文件和一個(gè)通用組件文件僅含有當(dāng)這些體積小于是,會(huì)故意將該模塊復(fù)制到三個(gè)文件中。 該文章內(nèi)容大致翻譯自 webpack 4: Code Splitting, chunk graph and the splitChunks optimization 原有的問(wèn)題 webpack...

    Caizhenhao 評(píng)論0 收藏0
  • React專(zhuān)題:react,redux以及react-redux常見(jiàn)一些面試題

    摘要:我們可以為元素添加屬性然后在回調(diào)函數(shù)中接受該元素在樹(shù)中的句柄,該值會(huì)作為回調(diào)函數(shù)的第一個(gè)參數(shù)返回。使用最常見(jiàn)的用法就是傳入一個(gè)對(duì)象。單向數(shù)據(jù)流,比較有序,有便于管理,它隨著視圖庫(kù)的開(kāi)發(fā)而被概念化。 面試中問(wèn)框架,經(jīng)常會(huì)問(wèn)到一些原理性的東西,明明一直在用,也知道怎么用, 但面試時(shí)卻答不上來(lái),也是挺尷尬的,就干脆把react相關(guān)的問(wèn)題查了下資料,再按自己的理解整理了下這些答案。 reac...

    darcrand 評(píng)論0 收藏0
  • 54個(gè)JAVA官方文檔重要術(shù)語(yǔ)

    摘要:近期在閱讀最新幾版的官方文檔過(guò)程中發(fā)現(xiàn)不少術(shù)語(yǔ)不清之處特發(fā)此文總結(jié)以下的術(shù)語(yǔ)大量在官方文檔中直接出現(xiàn)且直接如基本詞語(yǔ)一樣使用不理解它們會(huì)嚴(yán)重影響閱讀自適應(yīng)自旋鎖自適應(yīng)自旋鎖是一個(gè)允許線(xiàn)程在特定點(diǎn)自旋等待特定事件發(fā)生而不是直接進(jìn)行并等待該事件 近期在閱讀JAVA最新幾版的官方文檔過(guò)程中發(fā)現(xiàn)不少術(shù)語(yǔ)不清之處,特發(fā)此文總結(jié).以下的術(shù)語(yǔ)大量在官方文檔中直接出現(xiàn),且直接如基本詞語(yǔ)一樣使用,不理解...

    longmon 評(píng)論0 收藏0
  • 前端閱讀筆記 2016-11-25

    摘要:為了防止某些文檔或腳本加載別的域下的未知內(nèi)容,防止造成泄露隱私,破壞系統(tǒng)等行為發(fā)生。模式構(gòu)建函數(shù)響應(yīng)式前端架構(gòu)過(guò)程中學(xué)到的經(jīng)驗(yàn)?zāi)J降牟煌幵谟?,它主要?zhuān)注于恰當(dāng)?shù)貙?shí)現(xiàn)應(yīng)用程序狀態(tài)突變。嚴(yán)重情況下,會(huì)造成惡意的流量劫持等問(wèn)題。 今天是編輯周刊的日子。所以文章很多和周刊一樣。微信不能發(fā)鏈接,點(diǎn)了也木有用,所以請(qǐng)記得閱讀原文~ 發(fā)個(gè)動(dòng)圖娛樂(lè)下: 使用 SVG 動(dòng)畫(huà)制作游戲 使用 GASP ...

    KoreyLee 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<