本文转载自微信公众号《神光的编程秘籍》,作者神说必有光zxg。转载本文请联系神光编程秘籍公众号。现在我们一般不会把所有的代码都自己实现,而是基于第三方的包来开发,体现在src和node_modules目录下。不同项目src和node_modules(第三方包)所占比例不同。运行时查找第三方包的方式也不同:在node环境中,支持运行时查找node_modules。所以只需要部署src部分,然后安装相关依赖即可。浏览器环境不支持node_modules,需要打包成浏览器支持的形式。在跨端环境下,是上面哪一个?都不是,不同的跨端引擎的实现会不一样,跨端引擎会实现require,运行时可以找到模块(内置的和第三方的),但是node的搜索方式是自己定的。类似于node环境下的模块搜索,只是目录结构不同,所以需要自己实现xxxinstall。思路分析npm有自己的registryserver支持release包的下载,从registryserver下载。如果我们自己实现,就不需要实现这套,直接使用gitclone从gitlab下载源码即可。依赖分析要实现下载,首先要确定下载哪些。判断依赖的方式与打包工具不同:打包工具通过AST分析文件内容判断依赖,打包依赖。安装工具使用用户声明的依赖文件(package.json/bundle.json)来确定依赖并安装它们。这里我们调用包描述文件bundle.json,里面声明了依赖包:{"name":"xxx","dependencies":{"yyyy":"aaaa/bbbb#release/1111"}}通过分析bundle以项目根目录下的.json为入口,下载每一个依赖,解析bundle.json,然后继续下载每一个依赖,递归这个过程。这就是依赖分析的过程。这样,在依赖分析的过程中就下载了包,当依赖分析完成后,包的下载也就完成了。这是一种可行的思路。但是这种思路也存在问题,比如:版本冲突怎么办?循环依赖呢?解决版本冲突版本冲突就是多个包依赖同一个包,但是依赖的版本不同。这个时候,你必须选择一个版本来安装。我们可以简单地设置规则以使用更高版本。解决循环依赖包之间可能存在循环依赖(这就是为什么它被称为依赖图,而不是依赖树)。解决这个问题的方法是记录处理过的包。如果同版本的包分析过,这么久就不用分析了,取缓存就行了。这个思路是解决循环依赖问题的一般思路。我们已经解决了版本冲突和循环依赖的问题。还有其他问题吗?当出现版本冲突时,会下载版本最高的包,但是此时之前版本较低的包已经下载完毕,所以会多出一些无谓的下载,是否可以去掉这部分冗余下载.依赖分析和下载分离下载一些低版本的包是因为我们在依赖分析的过程中就下载了,那我们是不是只能在依赖分析的时候下载bundle.json进行分析,分析完就确定了依赖图呢?然后批量下载依赖?只是从gitlab下载bundle.json需要通过ssh协议下载,有点复杂。我们可以用更简单的思路来实现:gitclone--depth=1--branch=bbxxx加上--depth后,gitclone只会下载一个commit,速度会很快。虽然不如只下载bundle.json,但也是可以的(我试过下载所有commit时需要20s,下载单个commit只需要1s。)。这样我们在依赖分析的时候只下载一个commit到临时目录,分析依赖,解决冲突,确定依赖图后再分批下载。这时候使用gitclone下载所有commit。最后,删除临时目录。这样,我们通过分离依赖分析和下载,去掉了一些低版本包不必要的下载。下载速度将得到一些提升。全局缓存当本地有多个项目时,每个项目都是独立下载自己的依赖包,所以会出现一些公共包重复下载的情况。解决方案是全局缓存。分析完依赖,下载好各个依赖包后,首先查看该包是否全局存在。如果有,直接复制下来,拉下最新的代码。如果没有,先下载到全局,再复制到本地目录。通过多加一层全局缓存,我们实现了跨项目的依赖包复用。代码实现为了思路更清晰,下面会写依赖分析的伪代码依赖分析会递归处理bundle.json,分析依赖并下载到临时目录,记录分析的依赖。它将解决版本冲突和循环依赖。constbundleDeps={};functioninstallDeps(projectDir){constbundleJsonPath=path.resolve(projectDir,'bundle.json');constbundleInfo=JSON.parse(fs.readFileSync(bundleJsonPath));constbundleDeps=bundleInfo.dependencies;for(letdepName)inbundleDepsif(allDeps[depName]){if(allDeps[depName]和bundleDeps[depName]分支和版本相同){continue;//跳过安装}if(allDeps[depName]和bundleDeps[depName]分支和版本不同){if(bundleDeps[depName]version
