快给你的 node_modules 洗洗澡吧

895 阅读5分钟

最近在对一个单库多项目的仓库 m-site 做升级迁移,其中一项就跟 npm 有关。根据业务中的问题和检索到的资料,整理了一下思路,简单记录。

对你来说,你能了解到一些有意思的站点、npm最新的发展方向、经过我筛选的文章和一个有点卵用的包。

m-site 现状

  1. 安装慢(倒杯水再上个厕所可能还在安装)
  2. merge 代码后 lock 文件冲突 原因1:npm 版本不一致 原因2:多人协作时多个分支安装有重叠依赖的包后,合并分支
  3. --registry 安装源不一致,导致 lockfile 中 resolved 字段经常变化
  4. @types/xx 版本与 js module 不一致
  5. 幽灵依赖(下面细讲)
  6. 替身依赖

方案对比

为什么会有这么多的包管理工具?它们试图解决什么问题?

(图1) 包管理发展史(来源: time.graphics/line/602344)

Npm: 100s

npm 在这些问题下也做了很多优化,其中最明显的是从v3开始嵌套结构变成了扁平结构,但这带来了新的问题

  • 幽灵依赖问题 (phantom dependencies)。
  • 多重身问题,无法彻底解决重复依赖,譬如还存在 183 个重复依赖。(doppelgangers)。
  • 依赖结构的不确定性。(通过 依赖关系图 可以解决)
  • 扁平化算法的复杂度和性能损耗。

image.png

  • 庞大数量的网络IO和文件IO(根据官方数据计算,平均每秒全世界就有 7w 多个包被下载,要知道国内多数是从taobao下载,真实下载量更让人惊叹)

Yarn: 30s

最原汁原味(稳定)的优化

一开始没有什么“花招”,利用各种缓存策略,尽量的避免不必要的网络请求和 IO 操作

后来,,,yarn install --pnp(这款方案对使用方式有变化且兼容不好这里不研究)

Cnpm:我没试😂

  • 提供国内镜像源加速,10分钟同步一次官方包频率
  • 使用 npminstall 安装,借鉴 pnpm

缺点:

  • 软链接的缺点一处修改影响全局
  • windows 软链接跨磁盘问题
  • Electron 应用无法使用 pnpm

Pnpm: 23s

独特的非扁平目录结构,依赖查找关系如图:(真实目录有点难理解,可以对照图文多看几遍)

项目中引入bar@1.0.0

→ {$root}/node_modules/bar@1.0.0

→ 软链到 .pnpm/bar@1.0.0/node_modules/bar@1.0.0

→ 硬链接到全局缓存 ~/.pnpm

  • 内容寻址存储(content-addressable store)的方式全局缓存一份:~/.pnpm-store
  • 软链接到全局缓存,达到安装 node_modules 效果,减少 IO
  • 严格模式,避免bug:

安装后的目录结构

# 使用 ll 命令可以看到
# 软链不占用磁盘空间,相当于快捷方式,指向的真实目录
lrwxr-xr-x  *** source-map -> ../../source-map@0.5.7/node_modules/source-map

# 第二列数字大于1,就代表本文件是硬链接(硬链接实际上是文件副本,但是修改任何一个,其余的全部文件都会同步修改)
-rw-r--r--  2 username  staff   1.1K  2 18 18:10 

下一代包管理方式 Tnpm - rapid

着实🐂🍺,但是现在没开放,用不了😂,期待一下吧(*❦ω❦) --by 阿里巴巴 回顾npm安装的三大关键步骤,就可以看到

  1. Fast Resolving:解析依赖需要频繁的网络IO,非常耗时,一个项目得几千个请求才能生成依赖关系图lockfile
  2. Fast Fetching + Writing:下载聚合请求后的 tar 文件,请求数量减少,总体积也减小
  3. Without Writing: FUSE 文件系统直接操作 tar 文件

我做了什么

1. 限制 npm 版本:

官方 only-allow 包,只能限制工具类型却不能限制具体版本,所以我 fork 它,然后自己改了下,仓库在这,稍后发个 npm 包

package.json

{

  "scripts": {

    "preinstall": "node bin.js pnpm@6.0.0"

  }

}

2. 固定安装源:

.npmrc

# 可以按域指定 @foo/ 下所有包的安装源
@foo:registry=http://npm.your-registry.com

# 也可以一次性指定所有包的源
registry=http://npm.your-registry.com

# 下面是一些业务中其它工具安装时,用到的国内下载源
http_proxy=http://10.196.x.xxx

disturl=https://r.cnpmjs.org/node

sass_binary_site=https://r.cnpmjs.org/node-sass/

fse_binary_host_mirror=https://r.cnpmjs.org/fsevents

sentrycli_cdnurl=https://cdn.npm.taobao.org/dist/sentry-cli

3. 处理废弃的包:

删除 node_modules 用 npm i 重新安装你的包,会出现一大堆⚠️warning,仔细查看每一个警告修复它,越早处理越好。(接到的这个项目有些年头了,有些包明知道有问题也不敢随意升级😂,因为互相依赖的太多了)

4. ✅幽灵包添加到 package.json

这一项可以把pnpm的当做一个严格检查的工具,用 pnpm 重新安装,然后运行项目,把module not found的包依次安装到你的package.json中即可

"@better-scroll/pull-down": "^2.4.2",

"@better-scroll/pull-up": "^2.4.2",

"react": "16.14.0",

"react-dom": "^17.0.2",

"redux": "4.1.2",

使用 pnpm 达到了什么效果(当你做ppt时可以量化的数据)

1. 占用磁盘体积:安装项目越多,效果越明显

2. 安装时间:自然是肉眼可见的快

未来?

云构建? 云协作?

或许不再需要装包了,所有的环境都在云端,你只需要 import 就好。

参考链接: