Node.js源碼解析-啟動(dòng)-js部分
歡迎來我的博客閱讀:《Node.js源碼解析-啟動(dòng)-js部分》
Node.js 版本 8.x
Node.js 進(jìn)程啟動(dòng)時(shí),首先執(zhí)行 c / c++ 代碼圣蝎,然后 c / c++ 加載并執(zhí)行 lib/internal/bootstrap_node.js
并給予一個(gè) process
參數(shù)( 運(yùn)行上下文 )
// lib/internal/bootstrap_node.js 概覽
// Hello, and welcome to hacking node.js!
//
// This file is invoked by node::LoadEnvironment in src/node.cc, and is
// responsible for bootstrapping the node.js core. As special caution is given
// to the performance of the startup process, many dependencies are invoked
// lazily.
'use strict';
// 這里的 process 對(duì)象來自 c / c++宠漩,屬于原始數(shù)據(jù)
(function(process) {
// ...
startup();
})
加載 lib/internal/bootstrap_node.js
后嚣镜,直接執(zhí)行 startup()
函數(shù)
startup()
// lib/internal/bootstrap_node.js
function startup() {
// 下面幾行代碼使 process 具有 EventEmitter 的特性,比如說 on,emit
// BEGIN
const EventEmitter = NativeModule.require('events');
process._eventsCount = 0;
const origProcProto = Object.getPrototypeOf(process);
Object.setPrototypeOf(process, Object.create(EventEmitter.prototype, {
constructor: Object.getOwnPropertyDescriptor(origProcProto, 'constructor')
}));
// 相當(dāng)于 new EventEmitter() 土匀,不過上下文是 process
EventEmitter.call(process);
// END
// 一些初始化操作
// BEGIN
setupProcessObject();
// do this good and early, since it handles errors.
setupProcessFatal();
setupProcessICUVersions();
setupGlobalVariables();
if (!process._noBrowserGlobals) {
setupGlobalTimeouts();
setupGlobalConsole();
}
// END
// process 對(duì)象的初始化操作
// BEGIN
// 這里的 internal/process 模塊用于初始化 process
const _process = NativeModule.require('internal/process');
_process.setup_hrtime();
_process.setup_cpuUsage();
_process.setupMemoryUsage();
_process.setupConfig(NativeModule._source);
NativeModule.require('internal/process/warning').setup();
NativeModule.require('internal/process/next_tick').setup();
NativeModule.require('internal/process/stdio').setup();
_process.setupKillAndExit();
_process.setupSignalHandlers();
if (global.__coverage__)
NativeModule.require('internal/process/write-coverage').setup();
if (process.argv[1] !== '--debug-agent')
_process.setupChannel();
_process.setupRawDebug();
NativeModule.require('internal/url');
Object.defineProperty(process, 'argv0', {
enumerable: true,
configurable: false,
value: process.argv[0]
});
process.argv[0] = process.execPath;
// ...
// END
// 下面的 if-else 用來判斷 node 的運(yùn)行模式,我們只關(guān)注 node xx.js 的運(yùn)行模式
// if () {
// ...
} else {
// 執(zhí)行用戶代碼
// cluster 模塊的 hook
if (process.argv[1] && process.env.NODE_UNIQUE_ID) {
const cluster = NativeModule.require('cluster');
cluster._setupWorker();
// Make sure it's not accidentally inherited by child processes.
delete process.env.NODE_UNIQUE_ID;
}
if (process._eval != null && !process._forceRepl) {
// ...
} else if (process.argv[1] && process.argv[1] !== '-') {
// node app.js
// make process.argv[1] into a full path
const path = NativeModule.require('path');
// 變?yōu)榻^對(duì)路徑
process.argv[1] = path.resolve(process.argv[1]);
const Module = NativeModule.require('module');
// check if user passed `-c` or `--check` arguments to Node.
if (process._syntax_check_only != null) {
const fs = NativeModule.require('fs');
// read the source
const filename = Module._resolveFilename(process.argv[1]);
var source = fs.readFileSync(filename, 'utf-8');
checkScriptSyntax(source, filename);
process.exit(0);
}
preloadModules();
Module.runMain();
} else {
// REPL 或其他
}
}
}
startup()
最后調(diào)用 Module.runMain()
函數(shù)來加載并執(zhí)行用戶代碼形用。在執(zhí)行 startup()
函數(shù)的過程中恒削,多次用到了 NativeModule.require()
來加載模塊
NativeModule
NativeModule.require()
是專門用來加載 Node.js 內(nèi)置模塊的
// lib/internal/bootstrap_node.js
function NativeModule(id) {
this.filename = `${id}.js`;
this.id = id;
this.exports = {};
this.loaded = false;
this.loading = false;
}
NativeModule._source = process.binding('natives');
NativeModule._cache = {};
NativeModule.require = function(id) {
if (id === 'native_module') {
return NativeModule;
}
const cached = NativeModule.getCached(id);
if (cached && (cached.loaded || cached.loading)) {
return cached.exports;
}
if (!NativeModule.exists(id)) {
// Model the error off the internal/errors.js model, but
// do not use that module given that it could actually be
// the one causing the error if there's a bug in Node.js
const err = new Error(`No such built-in module: ${id}`);
err.code = 'ERR_UNKNOWN_BUILTIN_MODULE';
err.name = 'Error [ERR_UNKNOWN_BUILTIN_MODULE]';
throw err;
}
process.moduleLoadList.push(`NativeModule ${id}`);
const nativeModule = new NativeModule(id);
nativeModule.cache();
nativeModule.compile();
return nativeModule.exports;
};
NativeModule.getCached = function(id) {
return NativeModule._cache[id];
};
NativeModule.exists = function(id) {
return NativeModule._source.hasOwnProperty(id);
};
// ...
NativeModule.wrap = function(script) {
return NativeModule.wrapper[0] + script + NativeModule.wrapper[1];
};
NativeModule.wrapper = [
'(function (exports, require, module, __filename, __dirname) { ',
'\n});'
];
NativeModule.prototype.compile = function() {
var source = NativeModule.getSource(this.id);
source = NativeModule.wrap(source);
this.loading = true;
try {
const fn = runInThisContext(source, {
filename: this.filename,
lineOffset: 0,
displayErrors: true
});
fn(this.exports, NativeModule.require, this, this.filename);
this.loaded = true;
} finally {
this.loading = false;
}
};
NativeModule.prototype.cache = function() {
NativeModule._cache[this.id] = this;
};
NativeModule
有幾個(gè)重要的屬性和方法:
-
id
:NativeModule
的標(biāo)識(shí)符,例如events
尾序,internal/process
-
filename
:NativeModule
對(duì)應(yīng)源碼文件 -
exports
: 默認(rèn)值是{}
-
loaded
/loading
:NativeModule
狀態(tài) -
_cache
: 簡(jiǎn)單的模塊緩存 -
_source
: 模塊源碼資源 -
require()
: 先查詢緩存钓丰,緩存沒有則新建NativeModule
并編譯,返回exports
-
wrap()/wrapper
:wrapper
是對(duì)模塊上下文的包裹每币,如下:(function (exports, require, module, __filename, __dirname) { xxx });
-
compile()
: 將模塊源碼用wrapper
包裹后携丁,使用runInThisContext()
(類似eval()
)生成js
函數(shù),再執(zhí)行之
Module.runMain()
Node.js 啟動(dòng)完成后兰怠,調(diào)用 Module.runMain()
梦鉴,源碼如下:
// bootstrap main module.
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();
};
Module._load()
加載并執(zhí)行用戶代碼。至此 啟動(dòng)-js部分
已經(jīng)全部完成揭保,后續(xù)模塊加載部分肥橙,見 Node.js源碼解析-require背后
End
啟動(dòng)只是 Node.js 源碼的一小部分,除此之外還有大量的內(nèi)置模塊和 c / c++ 源碼
參考: