当前位置: 首页 > 科技观察

一篇带你了解Node.js

时间:2023-03-12 04:46:34 科技观察

1的文章。如何监听Node.js的所有功能这是一个危险的探索,但在某些场景下可能会有用。我想做的主要事情是劫持所有Node.js函数,插入钩子在函数执行前后做一些事情。但由于场景多、责任多,被劫持的风险很高。如果您在使用以下代码时遇到问题,可以提出问题。可以在执行代码之前预加载或加载以下代码。module-wrap.jsconst{Module}=require('module');functionbefore(...args){console.log(`beforecallfunctionargs:${args}`);}functionafter(...args){console.log(`aftercallfunctionresult:${args}`)}constoriginRequire=Module.prototype.require;//hacktomakeconsoleinitconsole.log('');functionnewRequire(...args){letexports=originRequire.call(this,...args);functionpatch(originFunc,key=originFunc.name){functiondummy(...args){//youcandosomethingbeforethefunctionwillbeexecutedbefore([key,...args]);letresult;//ifthefunctioncallbynew,wecallbynewtooif(new.target){result=neworiginFunc(...args);//maketheconstructorpointtonew.targetinsteadoforiginFuncbecausenew.targetmaybebeasubclassoforiginFuncresult.constructor=new.target;}else{result=originFunc.call(this,...args);}constparams=[key];if(result){params.push(result);}//你可以在函数执行后做一些事情(params);returnresult;}//weneedmergethefieldswhichiswritableoforiginFuncintodummyfor(const[key,descriptionInfo]ofObject.entries(Object.getOwnPropertyDescriptors(originFunc))){if(descriptionInfo.writable){Object.defineProperty(dummy,key,descriptionInfo);}}//将函数名称更改为originFuncObject.defineProperty(dummy,'name',{configurable:true,value:originFunc.name});Object.defineProperty(dummy,'name',{configurable:false});//theprototypeofdummyneedpointtooriginFunc.prototypedummy.prototype=originFunc.prototype;returndummy;}//wrapperallfunctionsineexport,butnowwedonnothandletheexportsrecursivelyif(Object.prototype.toString.call(exports)==='[objectObject]'){for(const[key,value]ofObject.entries(exports)){if(typeofvalue==='function'){exports[key]=补丁(值,键);}}}elseif(Object.prototype.toString.call(exports)==='[objectFunction]'){exports=patch(exports);}returnexports;}Module.prototype.require=newRequire;测试示例server.jsconsthttp=require('http');http.createServer((req,res)=>{res.end('ok');}).listen(8888);执行node-r./module-wraper.jsserver.js会看到输出beforecallfunctionargs:createServer,(req,res)=>{res.end('ok');}aftercallfunctionresult:createServer,[objectObject]钩子里你想干什么就干什么。2.如何实现直接执行ts代码ts-node相信很多同学都用过,它可以直接执行ts模块。下面的代码将做同样的事情。const{Module}=require('module');constfs=require('fs');constpath=require('path');constts=require('typescript');const{compileFunction}=process.binding('contextify');Module._extensions['.ts']=function(module,filename){constcontent=fs.readFileSync(filename,'utf8');const{outputText}=ts.transpileModule(content,{compilerOptions:{module:ts.ModuleKind.CommonJS}});constresult=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,然后调用V8的compileFunction执行js。3、如何编写js加载器Node.js的一些框架的实现模块会在启动前将所有的模块加载成一棵树。下面的代码是实现这个加载器的逻辑。constfs=require('fs');const{relative}=require('path');functionload(){returnnewPromise((resolve,reject)=>{constroot=process.cwd()+'/a';constfileTree={};constREGEXP=/\.(js|json|node)$/;constfilters=['node_modules','__tests__'];letrequest=0;letdone=false;function_load(currentPath){request++;fs.readdir(currentPath,(error,dirOrFiles)=>{request--;if(error){console.error(error);if(!done){done=true;reject(error);}}elseif(dirOrFiles.length){constabsolutePaths=dirOrFiles.filter((file)=>!filters.includes(file)).map((file)=>`${currentPath}/${file}`);for(leti=0;i{request--;if(error){console.error(error);if(!done){done=true;reject(error);}}else{if(stat.isDirectory()){_load(absolutePath);}else{try{if(REGEXP.test(absolutePath)){constabsolutePathWhithoutExt=absolutePath.replace(REGEXP,'');constrelativePathWhithoutExt=relative(root,absolutePathWhithoutExt);constpaths=relativePathWhithoutExt.split('/');letcurrentNode=fileTree;for(letj=0;j