当前位置: 首页 > Web前端 > HTML5

使用dpdm定位JavaScript-TypeScript中的循环依赖

时间:2023-04-05 11:15:47 HTML5

在写大型项目的时候,一不小心就会踏入直接循环依赖的坑。所谓直接循环依赖,是指在模块工厂函数中,存在对依赖于它的模块其他成员的直接调用。例如:假设有两个模块a.js和b.js,其中a.js的内容如下:constb=require('./b')exports.hello_a=functionhello_a(){return'a'}exports.hello_from_b=functionhello_from_b(){returnb.hello_b()}b.js的内容如下:consta=require('./a')//下面一行直接产生循环依赖exports.hello_from_a=a.hello_a()exports.hello_b=functionhello_b(){return'b'}这时候执行a.js的时候会报TypeError:a.hello_aisnotafunction错误,这个是因为模块a.js的工厂函数还在执行,而hello_a函数的声明还没有执行,而b.js直接调用了a.hello_a,所以认为这个变量不存在,报错。如果这不是一个函数而是一个变量,则不会报错,只是get得到的值是undefined,这样更难定位问题。虽然有一个叫madge的项目可以用来定位循环依赖,但是这个仓库在处理TypeScript时的输出简直就是一个形而上学的问题,经常莫名其妙的被忽略。不容忽视的文件,后面我会举例说明这个问题。于是花了一天时间造了一个轮子:dpdm,专门用来检测JavaScript和TypeScript项目中的循环依赖,目前可以检测到以下四种情况:CommonJS的require(...)函数调用ESM的staticimport...from...语句ESM的动态import(...)函数调用ESM的静态export...from...该语句可以满足大多数情况的需要,因为现在很少有人使用AMD和System。如何安装:dpdm可以在命令行上使用或者如果你想在命令行使用(推荐),建议全局安装,否则你可以在你的项目中安装#全局安装npmi-gdpdm#或者使用yarn:yarnglobaladddpdm#项目目录安装npmidpdm#或者使用yarn:yarnadddpdm在命令行中使用:直接使用命令dpdm[可选参数]<入口文件>,输出示例:默认会输出依赖树(tree),循环Dependency列表(循环)和警告信息(警告),可以使用参数关闭其中任何一个,例如dpdm--treefalse--warningfalse./src/index.ts关闭依赖树和警告信息(只显示循环依赖表)。您可以使用--output将结果输出到json文件。默认情况下,忽略node_modules中的内容。您可以使用--exclude''取消忽略。使用--help查看完整帮助文档:dpdm--helpdpdm[]entry...Options:--version显示版本号[boolean]--context上下文目录缩短路径,默认为process.cwd()[string]--extensions,--extcommaseparatedextensionstoresolve[string][default:".ts,.tsx,.mjs,.js,.jsx,.json"]--include包含文件名正则表达式字符串[字符串][default:"\.m?[tj]sx?$"]--excludeexcludedfilenamesregexpinstring[string][default:"/node_modules/"]--output,-o将json输出到文件[string]--tree打印树到stdout[boolean][default:true]--circular打印circulartostdout[boolean][default:true]--warning打印警告到stdout[boolean][default:true]-h,--helpShow代码中使用的help[boolean]:dpdm提供了几个API,可以查看包中提供的.d.ts提供的定义,或者去GitHub查看文档:例如:import{parseCircular,parseDependencyTree,parseWarnings,prettyCircular,prettyTree,prettyWarning,}from'dpdm';parseDependencyTree(['./src/**/*']/*入口,glob匹配,可以是数组*/,{/*参数列表,以下为默认参数*/context:process.cwd(),//前缀,用于缩写文件扩展名:['','.ts','.tsx','.mjs','.js','.jsx','.json'],//后缀包括:/\.m?[tj]sx?$/,//需要解析的文件exclude:/\/node_modules\//,//需要忽略的文件}).then((tree)=>{console.log('树:');console.log(prettyTree(tree,Object.keys(tree)));console.log('');console.log('Circular:');console.log(prettyCircular(parseCircular(tree)));console.log('');console.log('警告:');console.log(prettyWarning(parseWarnings(tree)));});该项目在GitHub上开源,地址为https://github.com/acrazing/dpdm,可以提issue或提交pr。顺便点个赞~