一个魔方组件,通常只需要依赖A即可,因为在安装A的时候会自动将B & C的内容打包生成到A中。
"dependencies": {
"A": "^1.0.0"
}
为什么要做环境隔离
之前,我们在编译某个基础依赖(例如 B)时:
举一个常见的场景来说明下这种模式的问题——小明在开发B,小红在开发C,两人都在测试环境进行了编译,导致发出去了两个正式包B@1.0.1 & C@1.0.1 。那么此时,如果小明开发测试完了,想要上个线,那么在服务器上执行到npm i的 时候,就会把小红还未测试完成的包C@1.0.1给安装到线上环境去。(其实在测试服务器上两个人的代码也是混合在一起的,不过毕竟是测试环境,影响较小)
从这个场景分析,可以发现有两个主要的问题:
针对上述的两个问题,对应的解决办法就是:
第一点就不说了,大致就是先npm view packageName versions获取包的所有版本,然后根据环境去获取最新的正式包版本或者是最新的beta版本,然后修改版本号再发包。
第二点,在更新依赖的时候,通过指定版本号的形式去安装我们最新发布的包。只不过在线上环境中,安装的是正式包,测试环境中安装的beta包。
看起来一切是那么的美好,但现实并不总是一帆风顺......
问题复现&解决初始化一个文件temp,并执行npm i先将所有依赖装一遍。
此时temp/node_modules下的情况为(此处只举例A和B,C与B情况相同,不再重复)
A: "A@1.0.0" A/node_modules下:无其他依赖
B: "B@1.0.0" B/node_modules下:无其他依赖
接下来,执行 npm i B@1.0.0-beta.1去单独更新B。
执行结果:
A: "A@1.0.0" A/node_modules下:B@1.0.0
B: "B@1.0.0-beta.1" B/node_modules下:无其他依赖
根据npm包安装的机制,默认情况下是不会使用beta包的,A依赖的B: "^1.0.0"需要使用稳定的版本,所以beta版本被放在最外层,而将之前的B@1.0.0放在了A/node_modules下。
魔方的基础依赖在使用前会在 A下执行一个externals命令(postinstall:npm run externals)将B的内容打成dist放在A目录下。但是node_modules依赖的查找顺序是先从当前文件目录下查找的,所以生成dist文件时使用的将会是A/node_modules/下的B@1.0.0,而不是最外层的B@1.0.0-beta.1
所以,我需要手动删除A/node_modules/B@1.0.0,再去执行externals命令。
那接下来我们再试试在此基础上更新A,npm i A@1.0.0-beta.1。
结果就是出现了更多冗余的依赖。。。
A: "A@1.0.0-beta.1" A/node_modules下:B@1.0.0、A@1.0.0
B: "B@1.0.0-beta.1" B/node_modules下:B@1.0.0、A@1.0.0
原因跟之前一样,我们安装的beta版本的A不符合B所依赖的A: "^1.0.0",就导致B下的node_modules中又多了一个A@1.0.0,然后这个A@1.0.0的又依赖一个稳定版本的B,所以在同级目录下还会再多一个B@1.0.0
同样的,我们仍需要先手动的去删除这些冗余的、不符合我们要求的依赖。
综上,为了确保我们项目中使用的都是我们刚发布的beta包,我们需要在每一次更新依赖时都执行一下这两条命令去清除冗余的依赖,然后再去执行打包命令。
rm -rf ./node_modules/A/node_modules/
rm -rf ./node_modules/B/node_modules/
rm -rf ./node_modules/C/node_modules/
cd node_modules/A
npm run externals
然而,到这一步还没完事,我将代码部署到测试服务器上后,经常出现依赖没有安装完成或安装完没有生成dist文件的情况,总是执行到一半就“中断”了。但是我在本地测试的时候却不会出现这种问题。
经过一步一步的排查,最终将问题定位到了这一行代码
await shelljs.shellExec()
查看shelljs.shellExec方法:
exports.shellExec = function (command, options = {}) {
return new Promise((resolve, reject) => {
Object.assign(options, {timeout: 300000});
shell.exec(command, options, () => {});
});
};
经常中断,难道是过了超时时间?我试着将超时时间从5min改到10min,部署至测试服务器,再次更新依赖,一切正常了......(不得不吐槽这个测试服务器的性能甚至不如我的Mac)
再一看编译时间,耗时>10min,!真棒。(取反)
项目名编译耗时A10:08B10:38
优化如此低效率的更新显然不能让人满意,而且由于魔方自身的原因,当编译基础依赖时,其他人是不能再部署其他魔方服务的,这就会阻塞其他人的流程,对开发人员的体验是十分差的。
「首先就是先分析问题找出原因:」
所以在一定程度上是问题1导致了问题2——安装了冗余的依赖,其中冗余的A会自动执行externals 命令导致耗时过久。
「所以我们首先需要解决的就是避免安装冗余的依赖:」
A需要依赖B,是因为需要将B的内容打包生成到A下使用。
B需要依赖A只是因为本地开发时方便调试。
如果去掉B的依赖项,那就可以在安装B@beta时避免额外安装A。但是,本地开发B的场景还是很多的,因此需要想个办法尽可能的减少由此带来的对开发体验的影响。
于是我在脚本中加入了一个自动检查并安装依赖的命令depcheck。
// dev之前先检查A:
// list可以列出当前工程下的A情况以及版本
// 如果没有会返回一个假值并走到npm i命令去安装A
"predev": "npm list A || npm i --no-save A"
这样,在安装B@beta的时候就不会额外安装A也不会额外执行externals命令了。
那么在安装A@beta的时候呢?还是会额外安装冗余的B然后自动执行externals,然后删除冗余的B,再手动执行一次externals。
「接下来需要针对A再次进行优化:」
由于A是强依赖B的所以不能去掉依赖项,冗余的B肯定是避免不了的,不过这又有什么关系呢,安装再删除一共也影响不了几秒钟。
但重点是A中有这样一个脚本命令:postinstall:npm run externals该命令是为了在开发时安装依赖等场景可以自动执行externals以减少操作次数&降低学习成本。
这就会导致第一次执行externals的时候实际使用的是冗余的稳定版本B,而非我们需要的最外层的B@beta,所以还需要删掉冗余依赖然后额外执行一次externals,这才是最耗时的部分。
“要是在npm i的时候可以不执行postinstall就好了”,带着这个期许,我找到了一个好用的参数——--ignore-scripts(忽略依赖中的脚本命令,不去执行任何脚本)
npm i A@beta --ignore-scripts
这样一来,在安装A的时候,也不会额外执行externals命令了!
「优化前后编译耗时对比:」
项目名优化前编译耗时优化后编译耗时A10:0804:42B10:3804:17
总结以上,就是在对魔方基础依赖环境隔离改造的思路和问题的解决:
作者:杨双星
来源:微信公众号:大转转FE
出处:https://mp.weixin.qq.com/s/RLW6Xh1vglX7S6an8qXaLw
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved