一篇帶你了解Node.js
1.如何監(jiān)聽 Node.js 的所有函數(shù)
這是一次危險的探索,但是或許某些場景下可以用到。主要想做的事情是劫持所有的 Node.js 函數(shù),在函數(shù)執(zhí)行前后,插入鉤子做些事情。但是由于場景很多而且負責(zé),劫持的風(fēng)險非常高,如果你使用以下代碼有問題,可以提個 issue。以下代碼可以通過預(yù)加載方式加載或者在你的代碼執(zhí)行前加載。
module-wrap.js
- const { Module } = require('module');
 - function before(...args) {
 - console.log(`before call function args: ${args}`);}
 - function after(...args) {
 - console.log(`after call function result: ${args}`)}const originRequire = Module.prototype.require;// hack to make console init
 - console.log('');
 - function newRequire(...args){
 - let exports = originRequire.call(this, ...args);
 - function patch(originFunc, key = originFunc.name) {
 - function dummy(...args) {
 - // you can do something before the function will be executed
 - before([key, ...args]);
 - let result;
 - // if the function call by new, we call by new too
 - if (new.target) {
 - result = new originFunc(...args);
 - // make the constructor point to new.target instead of originFunc because new.target maybe be a subclass of originFunc
 - result.constructor = new.target;
 - } else {
 - result = originFunc.call(this, ...args);
 - }
 - const params = [key];
 - if (result) {
 - params.push(result);
 - }
 - // you can do something after the function have executed
 - after(params);
 - return result;
 - }
 - // we need merge the fields which is writable of originFunc into dummy
 - for (const [key, descriptionInfo] of Object.entries(Object.getOwnPropertyDescriptors(originFunc))) {
 - if (descriptionInfo.writable) {
 - Object.defineProperty(dummy, key, descriptionInfo);
 - }
 - }
 - // change the function name to the name of originFunc
 - Object.defineProperty(dummy, 'name', { configurable: true, value: originFunc.name });
 - Object.defineProperty(dummy, 'name', { configurable: false });
 - // the prototype of dummy need point to originFunc.prototype
 - dummy.prototype = originFunc.prototype;
 - return dummy;
 - }
 - // wrapper all functions in export, but now we don not handle the exports recursively
 - if (Object.prototype.toString.call(exports) === '[object Object]') {
 - for (const [key, value] of Object.entries(exports)) {
 - if (typeof value === 'function') {
 - exports[key] = patch(value, key);
 - }
 - }
 - } else if (Object.prototype.toString.call(exports) === '[object Function]') {
 - exports = patch(exports);
 - }
 - return exports;}
 - Module.prototype.require = newRequire;
 
測試?yán)?。server.js
- const http = require('http');
 - http.createServer((req, res) => {
 - res.end('ok');}).listen(8888);
 
執(zhí)行 node -r ./module-wraper.js server.js 將會看到輸出
- before call function args: createServer,(req, res) => {
 - res.end('ok');}
 - after call function result: createServer,[object Object]
 
你可以在鉤子里做你想做的事情。
2.如何實現(xiàn)直接執(zhí)行 ts 代碼
ts-node 相信很多同學(xué)都使用過,它可以直接執(zhí)行 ts 模塊。下面的代碼同樣可以做到。
- const { Module } = require('module');const fs = require('fs');const path = require('path');const ts = require('typescript');const { compileFunction } = process.binding('contextify');
 - Module._extensions['.ts'] = function(module, filename) {
 - const content = fs.readFileSync(filename, 'utf8');
 - const { outputText } = ts.transpileModule(content, { compilerOptions: { module: ts.ModuleKind.CommonJS }});
 - const result = compileFunction(
 - outputText,
 - filename,
 - 0,
 - 0,
 - undefined,
 - false,
 - undefined,
 - [],
 - [
 - 'exports',
 - 'require',
 - 'module',
 - '__filename',
 - '__dirname',
 - ]
 - );
 - result.function.call(this, module.exports, (...args) => module.require(...args), module, filename, path.dirname(filename));};
 
原理很簡單,主要是給 Node.js 增加一個 ts 模塊的 加載器,在加載器里通過 typescript 包編譯 ts 成 js,然后再調(diào)用 V8 的 compileFunction 執(zhí)行 js。
3.如何寫一個 js loader
Node.js 的某些框架的實現(xiàn)模塊是在啟動前會加載所有的模塊成一個樹狀的結(jié)果,下面代碼是實現(xiàn)這個 loader 的邏輯。
- const fs = require('fs');const { relative } = require('path');
 - function load() {
 - return new Promise((resolve, reject) => {
 - const root = process.cwd() + '/a';
 - const fileTree = {};
 - const REGEXP = /\.(js|json|node)$/;
 - const filters = ['node_modules', '__tests__'];
 - let request = 0;
 - let done = false;
 - function _load(currentPath) {
 - request++;
 - fs.readdir(currentPath, (error, dirOrFiles) => {
 - request--;
 - if (error) {
 - console.error(error);
 - if (!done) {
 - done = true;
 - reject(error);
 - }
 - } else if (dirOrFiles.length) {
 - const absolutePaths = dirOrFiles.filter( (file) => !filters.includes(file) ).map((file) => `${currentPath}/${file}`);
 - for (let i = 0; i < absolutePaths.length; i++) {
 - const absolutePath = absolutePaths[i];
 - request++;
 - fs.stat(absolutePath, (error, stat) => {
 - request--;
 - if (error) {
 - console.error(error);
 - if (!done) {
 - done = true;
 - reject(error);
 - }
 - } else {
 - if (stat.isDirectory()) {
 - _load(absolutePath);
 - } else {
 - try {
 - if (REGEXP.test(absolutePath)) {
 - const absolutePathWhithoutExt = absolutePath.replace(REGEXP, '');
 - const relativePathWhithoutExt = relative(root, absolutePathWhithoutExt);
 - const paths = relativePathWhithoutExt.split('/');
 - let currentNode = fileTree;
 - for (let j = 0; j < paths.length - 1; j++) {
 - const path = paths[j];
 - if (typeof currentNode[path] === 'object' && currentNode[path] !== null) {
 - currentNode = currentNode[path];
 - } else {
 - currentNode = currentNode[path] = {};
 - }
 - }
 - currentNode[paths[paths.length - 1]] = require(absolutePath);
 - }
 - } catch(e) {
 - console.error(e);
 - if (!done) {
 - done = true;
 - reject(e);
 - }
 - }
 - }
 - }
 - if (!request && !done) {
 - done = true;
 - resolve(fileTree);
 - }
 - });
 - }
 - }
 - if (!request && !done) {
 - resolve(fileTree);
 - }
 - });
 - }
 - _load(root);
 - });}load().then(console.log).catch(console.error);
 
利用異步讀取的方式提高速度。
github 地址:
1. https://github.com/theanarkh/Node.js-Function-Wrapper
2. https://github.com/theanarkh/tiny-ts-node
3. https://github.com/theanarkh/Node.js-Loader















 
 
 











 
 
 
 