require gitlab
mudule對(duì)象化require最終會(huì)把每個(gè)模塊都轉(zhuǎn)化為對(duì)象
function Module(id, parent) { this.id = id; this.exports = {}; this.parent = parent; updateChildren(parent, this, false); this.filename = null; this.loaded = false; this.children = []; }require方法
用assert斷言輸入的合法性并調(diào)用_load方法還有一個(gè)調(diào)用_load的是
Module.runMain = function() { // Load the main module--the command line argument. Module._load(process.argv[1], null, true); // Handle any nextTicks added in the first tick of the program process._tickCallback(); };
這個(gè)我不是特別確定,但基本確定是給node xxx.js 這條命令調(diào)用的
load方法這里有很多是關(guān)于處理main的,核心的一段是
if (isMain) { process.mainModule = module; module.id = "."; }
這個(gè)佐證了上面runMain是給node xxx.js 這條命令調(diào)用的這個(gè)論點(diǎn),另外main模塊的id一定是 ".",并且parent一定是空。
另外在調(diào)用前會(huì)先查找模塊緩存的是否存在。
以下代碼為了簡化刪去main模塊的處理
//創(chuàng)建沒有原型的空對(duì)象 Module._cache = Object.create(null); Module._pathCache = Object.create(null); Module._load = function(request, parent, isMain) { if (parent) { debug("Module._load REQUEST %s parent: %s", request, parent.id); } var filename = Module._resolveFilename(request, parent, isMain); //緩存中是否存在 var cachedModule = Module._cache[filename]; if (cachedModule) { updateChildren(parent, cachedModule, true); return cachedModule.exports; } //是否是native模塊 if (NativeModule.nonInternalExists(filename)) { debug("load native module %s", request); return NativeModule.require(filename); } //其他模塊處理 var module = new Module(filename, parent); Module._cache[filename] = module; tryModuleLoad(module, filename); return module.exports; };
這個(gè)部分說明了加載模塊首先是判斷模塊名,之后是查找緩存,查找native 模塊,然后是其他模塊,最后的return是最為關(guān)鍵的,返回值永遠(yuǎn)是module的 exports
查找node_modules文件夾的規(guī)則Module._nodeModulePaths = function(from) { // guarantee that "from" is absolute. from = path.resolve(from); // Return early not only to avoid unnecessary work, but to *avoid* returning // an array of two items for a root: [ "http://node_modules", "/node_modules" ] if (from === "/") return ["/node_modules"]; // note: this approach *only* works when the path is guaranteed // to be absolute. Doing a fully-edge-case-correct path.split // that works on both Windows and Posix is non-trivial. const paths = []; var p = 0; var last = from.length; for (var i = from.length - 1; i >= 0; --i) { const code = from.charCodeAt(i); if (code === 47//*/*/) { if (p !== nmLen) paths.push(from.slice(0, last) + "/node_modules"); last = i; p = 0; } else if (p !== -1) { if (nmChars[p] === code) { ++p; } else { p = -1; } } }
從from開始逐層向上查找node_modules文件夾
Module._extensionsjs,json,node,mjs
每個(gè)后綴的文件都有對(duì)應(yīng)的打開方式
js 清除可能的BOM頭后加載
json json Parse
node .node 這是C/C++編寫的擴(kuò)展文件,通過dlopen()方法加載最后編譯生成的文件,可以當(dāng)作是一個(gè)系統(tǒng)調(diào)用
這部分代碼比較多,只看一段注釋即可
// given a module name, and a list of paths to test, returns the first
// matching file in the following precedence.
//
// require("a.
// -> a.
//
// require("a")
// -> a
// -> a.
// -> a/index.
node會(huì)去尋找路徑中的package.json文件并且會(huì)加載其中的main,并放入packagecache中,用main中指定的文件再去確認(rèn)絕對(duì)路徑然后加載
function readPackage(requestPath) { const entry = packageMainCache[requestPath]; if (entry) return entry; const jsonPath = path.resolve(requestPath, "package.json"); const json = internalModuleReadFile(path.toNamespacedPath(jsonPath)); if (json === undefined) { return false; } try { var pkg = packageMainCache[requestPath] = JSON.parse(json).main; } catch (e) { e.path = jsonPath; e.message = "Error parsing " + jsonPath + ": " + e.message; throw e; } return pkg; }compile
如何查找到的文件基本清楚了,之后就是最常用的js的compile了
js模塊的外層一定會(huì)被套上
Module.wrapper = [ "(function (exports, require, module, __filename, __dirname) { ", " });" ];
之后涉及了斷點(diǎn)的處理,是在vm模塊中的Srcript對(duì)象中的runInThisContext
后面有一句說明了為什么node中的所有文件也可以擁有Module的各種方法和特性 require 包括那些并不是main的文件,另外所有的模塊也是公用的模塊緩存,利用Module require中的return Module._load(path, this, /* isMain */ false);把自己的this作為parent作為Module對(duì)象的parent
var require = internalModule.makeRequireFunction(this);
makeRequireFunction的代碼
// Invoke with makeRequireFunction(module) where |module| is the Module object // to use as the context for the require() function. function makeRequireFunction(mod) { const Module = mod.constructor; function require(path) { try { exports.requireDepth += 1; return mod.require(path); } finally { exports.requireDepth -= 1; } } function resolve(request, options) { return Module._resolveFilename(request, mod, false, options); } require.resolve = resolve; function paths(request) { return Module._resolveLookupPaths(request, mod, true); } resolve.paths = paths; require.main = process.mainModule; // Enable support to add extra extension types. require.extensions = Module._extensions; require.cache = Module._cache; return require; }
執(zhí)行compiledWrapper,對(duì)應(yīng)wrapper插入的js
result = compiledWrapper.call(this.exports, this.exports, require, this, filename, dirname);
首先是compiledWrapper的this綁定在了Module自己exports上,自己的exports也作為了參數(shù)被注入,相當(dāng)于隱式的賦值exports就是compiledWrapper中的exports了這個(gè)就成了對(duì)外唯一的輸出了,其他的所有值都成了compiledWrapper的私有不會(huì)污染全局,require作為參數(shù)被注入,另外就是文件名 和路徑的注入
從上述處理加載的過程我們可以發(fā)現(xiàn)只要有require node就會(huì)繼續(xù)加載,另外CommonJs同步的特點(diǎn)也是在這體現(xiàn)出來的
對(duì)照學(xué)習(xí)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/93763.html
摘要:從一個(gè)對(duì)象里面提取需要的屬性這篇文章一直想寫了還想起那一夜我看到白天的代碼,實(shí)在太美了。 koa源碼lib主要文件有 application.js context.js request.js response.js application.js koa主要的邏輯處理代碼整個(gè)koa的處理 context.js 將req,res方法 掛載在這,生成ctx上下文對(duì)象 requests....
摘要:依賴信息是一個(gè)數(shù)組,比如上面的依賴數(shù)組是源碼如下是利用正則解析依賴的一個(gè)函數(shù)時(shí)間出發(fā)函數(shù)主要看這個(gè)部分注釋是防止拷貝該時(shí)間的回調(diào)函數(shù),防止修改,困惑了一下。對(duì)的賦值需要同步執(zhí)行,不能放在回調(diào)函數(shù)里。 sea.js想解決的問題 惱人的命名沖突 煩瑣的文件依賴 對(duì)應(yīng)帶來的好處 Sea.js 帶來的兩大好處: 通過 exports 暴露接口。這意味著不需要命名空間了,更不需要全局變量。...
摘要:閱讀好的框架的源碼有很多好處,從大神的視角去理解整個(gè)框架的設(shè)計(jì)思想。使用其實(shí)某個(gè)框架閱讀源碼的時(shí)候,首先我們要會(huì)去用這個(gè)框架,因?yàn)橛昧宋覀儾胖溃硞€(gè)是怎么用,哪里有坑,哪里設(shè)計(jì)的精妙。 閱讀好的框架的源碼有很多好處,從大神的視角去理解整個(gè)框架的設(shè)計(jì)思想。大到架構(gòu)設(shè)計(jì),小到可取的命名風(fēng)格,還有設(shè)計(jì)模式、實(shí)現(xiàn)某類功能使用到的數(shù)據(jù)結(jié)構(gòu)和算法等等。 使用koa 其實(shí)某個(gè)框架閱讀源碼的時(shí)候,首...
摘要:閱讀好的框架的源碼有很多好處,從大神的視角去理解整個(gè)框架的設(shè)計(jì)思想。使用其實(shí)某個(gè)框架閱讀源碼的時(shí)候,首先我們要會(huì)去用這個(gè)框架,因?yàn)橛昧宋覀儾胖?,某個(gè)是怎么用,哪里有坑,哪里設(shè)計(jì)的精妙。 閱讀好的框架的源碼有很多好處,從大神的視角去理解整個(gè)框架的設(shè)計(jì)思想。大到架構(gòu)設(shè)計(jì),小到可取的命名風(fēng)格,還有設(shè)計(jì)模式、實(shí)現(xiàn)某類功能使用到的數(shù)據(jù)結(jié)構(gòu)和算法等等。 使用koa 其實(shí)某個(gè)框架閱讀源碼的時(shí)候,首...
摘要:于是抱著知其然也要知其所以然的想法,開始閱讀的源代碼。問題讀源代碼時(shí),自然是帶著諸多問題的。源代碼如下在被處理完后,每當(dāng)有新請(qǐng)求,便會(huì)調(diào)用,去處理請(qǐng)求。接下來會(huì)繼續(xù)寫一些閱讀筆記,因?yàn)榭吹脑创a確實(shí)是獲益匪淺。 本筆記共四篇Koa源碼閱讀筆記(1) -- coKoa源碼閱讀筆記(2) -- composeKoa源碼閱讀筆記(3) -- 服務(wù)器の啟動(dòng)與請(qǐng)求處理Koa源碼閱讀筆記(4) -...
閱讀 545·2019-08-30 15:55
閱讀 956·2019-08-29 15:35
閱讀 1211·2019-08-29 13:48
閱讀 1924·2019-08-26 13:29
閱讀 2948·2019-08-23 18:26
閱讀 1261·2019-08-23 18:20
閱讀 2843·2019-08-23 16:43
閱讀 2718·2019-08-23 15:58