摘要:全局范圍生效,不需要。解析本地路徑首先來為你介紹對象,可以先在控制臺中看一下每一個模塊都有屬性來唯一標示它。通常是文件的完整路徑,但是在控制臺中一般顯示成。
本文作者:Jacob Beltran
編譯:胡子大哈翻譯原文:http://huziketang.com/blog/posts/detail?postId=58eaf471a58c240ae35bb8e3
英文連接:Requiring modules in Node.js: Everything you need to know
Node 中有兩個核心模塊來對模塊依賴進行管理:
require 模塊。全局范圍生效,不需要 require("require")。
module 模塊。全局范圍生效,不需要 require("module")。
你可以把 require 當(dāng)做是命令行,而把 module 當(dāng)做是所有引入模塊的組織者。
在 Node 中引入模塊并不是什么復(fù)雜的概念,見下面例子:
const config = require("/path/to/file");
require 引入的對象主要是函數(shù)。當(dāng) Node 調(diào)用 require() 函數(shù),并且傳遞一個文件路徑給它的時候,Node 會經(jīng)歷如下幾個步驟:
Resolving:找到文件的絕對路徑;
Loading:判斷文件內(nèi)容類型;
Wrapping:打包,給這個文件賦予一個私有作用范圍。這是使 require 和 module 模塊在本地引用的一種方法;
Evaluating:VM 對加載的代碼進行處理的地方;
Caching:當(dāng)再次需要用這個文件的時候,不需要重復(fù)一遍上面步驟。
本文中,我會用不同的例子來解釋上面的各個步驟,并且介紹在 Node 中它們對我們寫的模塊有什么樣的影響。
為了方便大家看文章和理解命令,我首先創(chuàng)建一個目錄,后面的操作都會在這個目錄中進行。
mkdir ~/learn-node && cd ~/learn-node
文章中接下來的部分都會在 ~/learn-node 文件夾下運行。
1. Resolving - 解析本地路徑首先來為你介紹 module 對象,可以先在控制臺中看一下:
~/learn-node $ node > module Module { id: "", exports: {}, parent: undefined, filename: null, loaded: false, children: [], paths: [ ... ] }
每一個模塊都有 id 屬性來唯一標示它。id 通常是文件的完整路徑,但是在控制臺中一般顯示成
Node 模塊和文件系統(tǒng)中的文件通常是一一對應(yīng)的,引入一個模塊需要把文件內(nèi)容加載到內(nèi)存中。因為 Node 有很多種方法引入一個文件(例如相對路徑,或者提前配置好的路徑),所以首先需要找到文件的絕對路徑。
如果我引入了一個 "find-me" 模塊,并沒有指定它的路徑的話:
require("find-me");
Node 會按照 module.paths 所指定的文件目錄順序依次尋找 find-me.js。
~/learn-node $ node > module.paths [ "/Users/samer/learn-node/repl/node_modules", "/Users/samer/learn-node/node_modules", "/Users/samer/node_modules", "/Users/node_modules", "/node_modules", "/Users/samer/.node_modules", "/Users/samer/.node_libraries", "/usr/local/Cellar/node/7.7.1/lib/node" ]
這個路徑列表基本上包含了從當(dāng)前目錄到根目錄的所有路徑中的 node_modules 目錄。其中還包含了一些不建議使用的遺留目錄。如果 Node 在上面所有的目錄中都沒有找到 find-me.js,會拋出一個“cannot find module error.”錯誤。
~/learn-node $ node > require("find-me") Error: Cannot find module "find-me" at Function.Module._resolveFilename (module.js:470:15) at Function.Module._load (module.js:418:25) at Module.require (module.js:498:17) at require (internal/module.js:20:19) at repl:1:1 at ContextifyScript.Script.runInThisContext (vm.js:23:33) at REPLServer.defaultEval (repl.js:336:29) at bound (domain.js:280:14) at REPLServer.runBound [as eval] (domain.js:293:12) at REPLServer.onLine (repl.js:533:10)
如果現(xiàn)在創(chuàng)建一個 node_modules,并把 find-me.js 放進去,那么 require("find-me") 就能找到了。
~/learn-node $ mkdir node_modules ~/learn-node $ echo "console.log("I am not lost");" > node_modules/find-me.js ~/learn-node $ node > require("find-me"); I am not lost {} >
假設(shè)還有另一個目錄中存在 find-me.js,例如在 home/node_modules 目錄中有另一個 find-me.js 文件。
$ mkdir ~/node_modules $ echo "console.log("I am the root of all problems");" > ~/node_modules/find-me.js
當(dāng)我們從 learn-node 目錄中執(zhí)行 require("find-me") 的時候,由于 learn-node 有自己的 node_modules/find-me.js,這時不會加載 home 目錄下的 find-me.js:
~/learn-node $ node > require("find-me") I am not lost {} >
假設(shè)我們把 learn-node 目錄下的 node_modules 移到 ~/learn-node,再重新執(zhí)行 require("find-me") 的話,按照上面規(guī)定的順序查找文件,這時候 home 目錄下的 node_modules 就會被使用了。
~/learn-node $ rm -r node_modules/ ~/learn-node $ node > require("find-me") I am the root of all problems {} >require 一個文件夾
模塊不一定非要是文件,也可以是個文件夾。我們可以在 node_modules 中創(chuàng)建一個 find-me 文件夾,并且放一個 index.js 文件在其中。那么執(zhí)行 require("find-me") 將會使用 index.js 文件:
~/learn-node $ mkdir -p node_modules/find-me ~/learn-node $ echo "console.log("Found again.");" > node_modules/find-me/index.js ~/learn-node $ node > require("find-me"); Found again. {} >
這里注意,我們本目錄下創(chuàng)建了 node_modules 文件夾,就不會使用 home 目錄下的 node_modules 了。
當(dāng)引入一個文件夾的時候,默認會去找 index.js 文件,這也可以手動控制指定到其他文件,利用 package.json 的 main 屬性就可以。例如,我們執(zhí)行 require("find-me"),并且要從 find-me 文件夾下的 start.js 文件開始解析,那么用 package.json 的做法如下:
~/learn-node $ echo "console.log("I rule");" > node_modules/find-me/start.js ~/learn-node $ echo "{ "name": "find-me-folder", "main": "start.js" }" > node_modules/find-me/package.json ~/learn-node $ node > require("find-me"); I rule {} >require.resolve
如果你只是想解析模塊,而不執(zhí)行的話,可以使用 require.resolve 函數(shù)。它和主 require 函數(shù)所做的事情一模一樣,除了不加載文件。當(dāng)沒找到文件的時候也會拋出錯誤,如果找到會返回文件的完整路徑。
> require.resolve("find-me"); "/Users/samer/learn-node/node_modules/find-me/start.js" > require.resolve("not-there"); Error: Cannot find module "not-there" at Function.Module._resolveFilename (module.js:470:15) at Function.resolve (internal/module.js:27:19) at repl:1:9 at ContextifyScript.Script.runInThisContext (vm.js:23:33) at REPLServer.defaultEval (repl.js:336:29) at bound (domain.js:280:14) at REPLServer.runBound [as eval] (domain.js:293:12) at REPLServer.onLine (repl.js:533:10) at emitOne (events.js:101:20) at REPLServer.emit (events.js:191:7) >
它可以用于檢查一個包是否已經(jīng)安裝,只有當(dāng)包存在的時候才使用該包。
相對路徑和絕對路徑除了可以把模塊放在 node_modules 目錄中,還有更自由的方法。我們可以把模塊放在任何地方,然后通過相對路徑(./ 和 ../)或者絕對路徑(/)來指定文件路徑。
例如 find-me.js 文件是在 lib 目錄下,而不是在 node_modules 下,我們可以這樣引入:
require("./lib/find-me");文件的 parent-child 關(guān)系
創(chuàng)建一個文件 lib/util.js 并且寫一行 console.log 在里面來標識它,當(dāng)然,這個 console.log 就是模塊本身。
~/learn-node $ mkdir lib ~/learn-node $ echo "console.log("In util", module);" > lib/util.js
在 index.js 中寫上將要執(zhí)行的 node 命令,并且在 index.js 中引入 lib/util.js:
~/learn-node $ echo "console.log("In index", module); require("./lib/util");" > index.js
現(xiàn)在在 node 中執(zhí)行 index.js:
~/learn-node $ node index.js In index Module { id: ".", exports: {}, parent: null, filename: "/Users/samer/learn-node/index.js", loaded: false, children: [], paths: [ ... ] } In util Module { id: "/Users/samer/learn-node/lib/util.js", exports: {}, parent: Module { id: ".", exports: {}, parent: null, filename: "/Users/samer/learn-node/index.js", loaded: false, children: [ [Circular] ], paths: [...] }, filename: "/Users/samer/learn-node/lib/util.js", loaded: false, children: [], paths: [...] }
注意到這里,index 模塊(id:".")被列到了 lib/util 的 parent 屬性中。而 lib/util 并沒有被列到 index 的 children 屬性,而是用一個 [Circular] 代替的。這是因為這是個循環(huán)引用,如果這里使用 lib/util 的話,那就變成一個無限循環(huán)了。這就是為什么在 index 中使用 [Circular] 來替代 lib/util。
那么重點來了,如果在 lib/util 中引入了 index 模塊會怎么樣?這就是我們所謂的模塊循環(huán)依賴問題,在 Node 中是允許這樣做的。
但是 Node 如何處理這種情況呢?為了更好地理解這一問題,我們先來了解一下模塊對象的其他知識。
2. Loading - exports,module.exports,和模塊的同步加載在所有的模塊中,exports 都是一個特殊的對象。如果你有注意的話,上面我們每次打印模塊信息的時候,都有一個是空值的 exports 屬性。我們可以給這個 exports 對象加任何想加的屬性,例如在 index.js 和 lib/util.js 中給它添加一個 id 屬性:
// 在 lib/util.js 的最上面添加這行 exports.id = "lib/util"; // 在 index.js 的最上面添加這行 exports.id = "index";
執(zhí)行 index.js,可以看到我們添加的屬性已經(jīng)存在于模塊對象中:
~/learn-node $ node index.js In index Module { id: ".", exports: { id: "index" }, loaded: false, ... } In util Module { id: "/Users/samer/learn-node/lib/util.js", exports: { id: "lib/util" }, parent: Module { id: ".", exports: { id: "index" }, loaded: false, ... }, loaded: false, ... }
上面為了輸出結(jié)果簡潔,我刪掉了一些屬性。你可以往 exports 對象中添加任意多的屬性,甚至可以把 exports 對象變成其他類型,比如把 exports 對象變成函數(shù),做法如下:
// 在 index.js 的 console.log 前面添加這行 module.exports = function() {};
當(dāng)你執(zhí)行 index.js 的時候,你會看到如下信息:
~/learn-node $ node index.js In index Module { id: ".", exports: [Function], loaded: false, ... }
這里注意我們沒有使用 export = function() {} 來改變 exports 對象。沒有這樣做是因為在模塊中的 exports 變量實際上是 module.exports 的一個引用,而 module.exports 才是控制所有對外屬性的。exports 和 module.exports 指向同一塊內(nèi)存,如果把 exports 指向一個函數(shù),那么相當(dāng)于改變了 exports 的指向,exports 就不再是引用了。即便你改變了 exports,module.exports 也是不變的。
模塊的 module.exports 是一個模塊的對外接口,就是當(dāng)你使用 require 函數(shù)時所返回的東西。例如把 index.js 中的代碼改一下:
const UTIL = require("./lib/util"); console.log("UTIL:", UTIL);
上面的代碼將會捕獲 lib/util 中輸出的屬性,賦值給 UTIL 常量。當(dāng)執(zhí)行 index.js 的時候,最后一行將會輸出:
UTIL: { id: "lib/util" }
接下來聊一下 loaded 屬性。上面我們每次輸出模塊信息,都能看到一個 loaded 屬性,值是 false。
module 模塊使用 loaded 屬性來追蹤哪些模塊已經(jīng)加載完畢,哪些模塊正在加載。例如我們可以調(diào)用 setImmediate 來打印 module 對象,用它可以看到 index.js 的完全加載信息:
// In index.js setImmediate(() => { console.log("The index.js module object is now loaded!", module) });
輸出結(jié)果:
The index.js module object is now loaded! Module { id: ".", exports: [Function], parent: null, filename: "/Users/samer/learn-node/index.js", loaded: true, children: [ Module { id: "/Users/samer/learn-node/lib/util.js", exports: [Object], parent: [Circular], filename: "/Users/samer/learn-node/lib/util.js", loaded: true, children: [], paths: [Object] } ], paths: [ "/Users/samer/learn-node/node_modules", "/Users/samer/node_modules", "/Users/node_modules", "/node_modules" ] }
可以注意到 lib/util.js 和 index.js 都已經(jīng)加載完畢了。
當(dāng)一個模塊加載完成的時候,exports 對象才完整,整個加載的過程都是同步的。這也是為什么在一個事件循環(huán)后所有的模塊都處于完全加載狀態(tài)的原因。
這也意味著不能異步改變 exports 對象,例如,對任何模塊做下面這樣的事情:
fs.readFile("/etc/passwd", (err, data) => { if (err) throw err; exports.data = data; // Will not work. });模塊循環(huán)依賴
我們現(xiàn)在來回答上面說到的循環(huán)依賴的問題:模塊 1 依賴模塊 2,模塊 2 也依賴模塊 1,會發(fā)生什么?
現(xiàn)在來創(chuàng)建兩個文件,lib/module1.js 和 lib/module2.js,并且讓它們相互引用:
// lib/module1.js exports.a = 1; require("./module2"); exports.b = 2; exports.c = 3; // lib/module2.js const Module1 = require("./module1"); console.log("Module1 is partially loaded here", Module1);
接下來執(zhí)行 module1.js,可以看到:
~/learn-node $ node lib/module1.js Module1 is partially loaded here { a: 1 }
在 module1 完全加載之前需要先加載 module2,而 module2 的加載又需要 module1。這種狀態(tài)下,我們從 exports 對象中能得到的就是在發(fā)生循環(huán)依賴之前的這部分。上面代碼中,只有 a 屬性被引入,因為 b 和 c 都需要在引入 module2 之后才能加載進來。
Node 使這個問題簡單化,在一個模塊加載期間開始創(chuàng)建 exports 對象。如果它需要引入其他模塊,并且有循環(huán)依賴,那么只能部分引入,也就是只能引入發(fā)生循環(huán)依賴之前所定義的這部分。
JSON 和 C/C++ 擴展文件我們可以使用 require 函數(shù)本地引入 JSON 文件和 C++ 擴展文件,理論上來講,不需要指定其擴展名。
如果沒有指定擴展名,Node 會先嘗試將其按 .js 文件來解析,如果不是 .js 文件,再嘗試按 .json 文件來解析。如果都不是,會嘗試按 .node 二進制文件解析。但是為了使程序更清晰,當(dāng)引入除了 .js 文件的時候,你都應(yīng)該指定文件擴展名。
如果你要操作的文件是一些靜態(tài)配置值,或者是需要定期從外部文件中讀取的值,那么引入 JSON 是很好的一個選擇。例如有如下的 config.json 文件:
{ "host": "localhost", "port": 8080 }
我們可以直接像這樣引用:
const { host, port } = require("./config"); console.log(`Server will run at http://${host}:${port}`);
運行上面的代碼會得到這樣的輸出:
Server will run at http://localhost:8080
如果 Node 按 .js 和 .json 解析都失敗的話,它會按 .node 解析,把這個文件當(dāng)做一個已編譯的擴展模塊來解析。
Node 文檔中有一個 C++ 寫的示例擴展文件,它只暴露出一個 hello() 函數(shù),并且函數(shù)輸出 “world”。
你可以使用 node-gyp 包編譯 .cc 文件,生成 .addon 文件。只需要配置 binding.gyp 文件來告訴 node-gyp 需要做什么就可以了。
當(dāng)你有了 addon.node 文件(名字你可以在 binding.gyp 中隨意配置)以后,你就可以在本地像引入其他模塊一樣引入它了:
const addon = require("./addon"); console.log(addon.hello());
可以通過 require.extensions 來查看對三種文件的支持情況:
可以清晰地看到 Node 對每種擴展名所使用的函數(shù)及其操作:對 .js 文件使用 module._compile;對 .json 文件使用 JSON.parse;對 .node 文件使用 process.dlopen。
3. Wrapping - 你在 Node 中所寫的所有代碼都會被打包成函數(shù)Node 的打包模塊不是很好理解,首先要先知道 exports / module.exports 的關(guān)系。
我們可以用 exports 對象來輸出屬性,但是不能直接對 exports 進行賦值(替換整個 exports 對象),因為它僅僅是 module.exports 的引用。
exports.id = 42; // This is ok. exports = { id: 42 }; // This will not work. module.exports = { id: 42 }; // This is ok.
在介紹 Node 的打包過程之前先來了解另一個問題,通常情況下,在瀏覽器中我們在腳本中定義一個變量:
var answer = 42;
這種方式定義以后,answer 變量就是一個全局變量了。其他腳本中依然可以訪問。而 Node 中不是這樣,你在一個模塊中定義一個變量,程序的其他模塊是不能訪問的。Node 是如何做到的呢?
答案很簡單,在編譯成模塊之前,Node 把模塊代碼都打包成函數(shù),可以用 module 的 wrapper 屬性來查看。
~ $ node > require("module").wrapper [ "(function (exports, require, module, __filename, __dirname) { ", " });" ] >
Node 并不直接執(zhí)行你所寫的代碼,而是把你的代碼打包成函數(shù)后,執(zhí)行這個函數(shù)。這就是為什么一個模塊的頂層變量的作用域依然僅限于本模塊的原因。
這個打包函數(shù)有 5 個參數(shù):exports,require,module,__filename,__dirname。函數(shù)使變量看起來全局生效,但實際上只在模塊內(nèi)生效。所有的這些參數(shù)都在 Node 執(zhí)行函數(shù)時賦值。exports 定義成 module.exports 的引用;require 和 module 都指定為將要執(zhí)行的這個函數(shù);__filename 和 __dirname 指這個打包模塊的絕對路徑和目錄路徑。
在腳本的第一行輸入有問題的代碼,就能看到 Node 打包的行為;
~/learn-node $ echo "euaohseu" > bad.js ~/learn-node $ node bad.js ~/bad.js:1 (function (exports, require, module, __filename, __dirname) { euaohseu ^ ReferenceError: euaohseu is not defined
注意這里報告出錯誤的就是打包函數(shù)。
另外,模塊都打包成函數(shù)了,我們可以使用 arguments 關(guān)鍵字來訪問函數(shù)的參數(shù):
~/learn-node $ echo "console.log(arguments)" > index.js ~/learn-node $ node index.js { "0": {}, "1": { [Function: require] resolve: [Function: resolve], main: Module { id: ".", exports: {}, parent: null, filename: "/Users/samer/index.js", loaded: false, children: [], paths: [Object] }, extensions: { ... }, cache: { "/Users/samer/index.js": [Object] } }, "2": Module { id: ".", exports: {}, parent: null, filename: "/Users/samer/index.js", loaded: false, children: [], paths: [ ... ] }, "3": "/Users/samer/index.js", "4": "/Users/samer" }
第一個參數(shù)是 exports 對象,初始為空;require 和 module 對象都是即將執(zhí)行的 index.js 的實例;最后兩個參數(shù)是文件路徑和目錄路徑。
打包函數(shù)的返回值是 module.exports。在模塊內(nèi)部,可以使用 exports 對象來改變 module.exports 屬性,但是不能對 exports 重新賦值,因為它只是 module.exports 的引用。
相當(dāng)于如下代碼:
function (require, module, __filename, __dirname) { let exports = module.exports; // Your Code... return module.exports; }
如果對 exports 重新賦值(改變整個 exports 對象),那它就不是 module.exports 的引用了。這是 JavaScript 引用的工作原理,不僅僅是在這里是這樣。
4. Evaluating - require 對象require 沒有什么特別的,通常作為一個函數(shù)返回 module.exports 對象,函數(shù)參數(shù)是一個模塊名或者一個路徑。如果你想的話,盡可以根據(jù)自己的邏輯重寫 require 對象。
例如,為了達到測試的目的,我們希望所有的 require 都默認返回一個 mock 值來替代真實的模塊返回值。可以簡單地實現(xiàn)如下:
require = function() { return { mocked: true }; }
這樣重寫了 require 以后,每個 require("something") 調(diào)用都會返回一個模擬對象。
require 對象也有自己的屬性。上面已經(jīng)見過了 resolve 屬性,它的任務(wù)是處理引入模塊過程中的解析步驟,上面還提到過 require.extensions 也是 require 的屬性。還有 require.main,它用于判斷一個腳本是否應(yīng)該被引入還是直接執(zhí)行。
例如,在 print-in-frame.js 中有一個 printInFrame 函數(shù)。
// In print-in-frame.js const printInFrame = (size, header) => { console.log("*".repeat(size)); console.log(header); console.log("*".repeat(size)); };
函數(shù)有兩個參數(shù),一個是數(shù)字類型參數(shù) size,一個是字符串類型參數(shù) header。函數(shù)功能很簡單,這里不贅述。
我們想用兩種方式使用這個文件:
1.直接使用命令行:
~/learn-node $ node print-in-frame 8 Hello
傳遞 8 和 “Hello” 兩個參數(shù)進去,打印 8 個星星包裹下的 “Hello”。
2.使用 require。假設(shè)所引入的模塊對外接口是 printInFrame 函數(shù),我們可以這樣調(diào)用:
const print = require("./print-in-frame"); print(5, "Hey");
傳遞的參數(shù)是 5 和 “Hey”。
這是兩種不同的用法,我們需要一種方法來判斷這個文件是作為獨立的腳本來運行,還是需要被引入到其他的腳本中才能執(zhí)行。可以使用簡單的 if 語句來實現(xiàn):
if (require.main === module) { // 這個文件直接執(zhí)行(不需要 require) }
繼續(xù)演化,可以使用不同的調(diào)用方式來實現(xiàn)最初的需求:
// In print-in-frame.js const printInFrame = (size, header) => { console.log("*".repeat(size)); console.log(header); console.log("*".repeat(size)); }; if (require.main === module) { printInFrame(process.argv[2], process.argv[3]); } else { module.exports = printInFrame; }
當(dāng)文件不需要被 require 時,直接通過 process.argv 調(diào)用 printInFrame 函數(shù)即可。否則直接把 module.exports 變成 printInFrame 就可以了,即模塊接口是 printInFrame。
5. Caching - 所有的模塊都會被緩存對緩存的理解特別重要,我用簡單的例子來解釋緩存。
假設(shè)你有一個 ascii-art.js 文件,打印很酷的 header:
我們想要在每次 require 這個文件的時候,都打印出 header。所以把這個文件引入兩次:
require("./ascii-art") // 顯示 header require("./ascii-art") // 不顯示 header.
第二個 require 不會顯示 header,因為模塊被緩存了。Node 把第一個調(diào)用緩存起來,第二次調(diào)用的時候就不加載文件了。
可以在第一次引入文件以后,使用 require.cache 來看一下都緩存了什么。緩存中實際上是一個對象,這個對象中包含了引入模塊的屬性。我們可以從 require.cache 中把相應(yīng)的屬性刪掉,以使緩存失效,這樣 Node 就會重新加載模塊并且將其重新緩存起來。
對于這個問題,這并不是最有效的解決方案。最簡單的解決方案是把 ascii-art.js 中的打印代碼打包成一個函數(shù),并且 export 這個函數(shù)。這樣當(dāng)我們引入 ascii-art.js 文件時,我們獲取到的是這個函數(shù),所以可以每次都能打印出想要的內(nèi)容了:
require("./ascii-art")() // 打印出 header. require("./ascii-art")() // 也會打印出 header.總結(jié)
這就是我所要介紹的內(nèi)容。回顧一下通篇,分別講述了:
Resolving
Loading
Wrapping
Evaluating
Caching
即解析、加載、打包、VM功能處理和緩存五大步驟,以及五大步驟中每個步驟都涉及到了什么內(nèi)容。
如果本文對你有幫助,歡迎關(guān)注我的專欄-前端大哈,定期發(fā)布高質(zhì)量前端文章。
我最近正在寫一本《React.js 小書》,對 React.js 感興趣的童鞋,歡迎指點。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/82475.html
摘要:該模塊實現(xiàn)方案主要包含與這兩個關(guān)鍵字,其允許某個模塊對外暴露部分接口并且由其他模塊導(dǎo)入使用。由于在服務(wù)端的流行,的模塊形式被不正確地稱為。以上所描述的模塊載入機制均定義在中。 最近一直在搞基礎(chǔ)的東西,弄了一個持續(xù)更新的github筆記,可以去看看,誠意之作(本來就是寫給自己看的……)鏈接地址:Front-End-Basics 此篇文章的地址:JavaScript的模塊 基礎(chǔ)筆記...
摘要:希望幫助更多的前端愛好者學(xué)習(xí)。前端開發(fā)者指南作者科迪林黎,由前端大師傾情贊助。翻譯最佳實踐譯者張捷滬江前端開發(fā)工程師當(dāng)你問起有關(guān)與時,老司機們首先就會告訴你其實是個沒有網(wǎng)絡(luò)請求功能的庫。 前端基礎(chǔ)面試題(JS部分) 前端基礎(chǔ)面試題(JS部分) 學(xué)習(xí) React.js 比你想象的要簡單 原文地址:Learning React.js is easier than you think 原文作...
摘要:簡介輕量級的項目內(nèi)核性能監(jiān)控分析工具,在默認模式下,只需要在項目入口文件一次,無需改動任何業(yè)務(wù)代碼即可開啟內(nèi)核級別的性能監(jiān)控分析。訪問監(jiān)控頁面打開你的瀏覽器,訪問,即可看到進程界面。如果這個項目對您有幫助,給個鼓勵一下也是很開心的事情 Easy-Monitor 2.0 I. 簡介 輕量級的 Node.js 項目內(nèi)核性能監(jiān)控 + 分析工具,在默認模式下,只需要在項目入口文件 requir...
摘要:在源碼中也可以看到,在執(zhí)行之前動態(tài)的引入了這些解釋器模塊。因為認為如果你要使用,那么一定會有對應(yīng)的依賴,這個模塊就是與同級的依賴,也就是說可以放心的進行,大致這樣的結(jié)構(gòu)的位置在這里執(zhí)行腳本以及一個相反的栗子 NPM是Node.js的包管理工具,隨著Node.js的出現(xiàn),以及前端開發(fā)開始使用gulp、webpack、rollup以及其他各種優(yōu)秀的編譯打包工具(大多數(shù)采用Node.js來實...
摘要:關(guān)于本文是一個開源的應(yīng)用容器引擎,讓開發(fā)者可以打包他們的應(yīng)用以及依賴包到一個可移植的容器中,然后發(fā)布到任何流行的機器上。比如說,通過堆棧來開發(fā)應(yīng)用的每個部分。開發(fā)環(huán)境之前提到的,能夠很容易的引入文件并且使用它們。 關(guān)于本文Docker 是一個開源的應(yīng)用容器引擎,讓開發(fā)者可以打包他們的應(yīng)用以及依賴包到一個可移植的容器中,然后發(fā)布到任何流行的 Linux 機器上。幾乎沒有性能開銷,可以很容...
閱讀 2631·2021-10-12 10:12
閱讀 784·2019-08-29 17:25
閱讀 2785·2019-08-29 17:24
閱讀 3215·2019-08-29 17:19
閱讀 1802·2019-08-29 15:39
閱讀 3046·2019-08-26 16:50
閱讀 1990·2019-08-26 12:17
閱讀 2699·2019-08-26 12:16