地狱JavaScript依赖之路

每个JavaScript项目都始于良好的意图,即其创建者承诺自己在开发过程中不要使用过多的NPM软件包。但是,即使开发人员做出了巨大的努力来兑现这一承诺,NPM软件包也逐渐渗透到他们的项目中。文件大小package.json随时间增长。随着package-lock.json依赖性安装,出现了真正的恐怖,表现为软件包的增加和删除,在下一个PR中尤为明显。 “一切都很好,”团队负责人说。团队的其他成员点头表示同意。还要做什么?我们都喜欢JavaScript生态系统还活得很好的事实。我们不需要每次都重新发明轮子,而是尝试解决开源社区已经解决的问题。





假设您要创建一个博客并想要使用Gatsby.js。尝试根据您的项目添加此网站生成器。现在,恭喜。您的项目仅具有19,000个其他依赖项。这个是正常的?JavaScript依赖树会变得多么复杂?依赖树如何变成地狱?让我们弄清楚。

什么是JavaScript包?


NPM(节点软件包管理器,节点软件包管理器)存储世界上最大的软件包注册表。这些是JavaScript包。NPM不仅仅是RubyGems,PyPi和Maven的总和。可以基于对模块计数项目数据的分析得出结论,该项目监视流行注册表中的软件包数量。


有关流行注册表中软件包数量的数据,

您可能会认为此图中显示了大量代码。事情是这样的。为了将项目转换为NPM软件包,此软件包必须具有filepackage.json这样的软件包可以发送到NPM注册中心。

什么是package.json?


这是它要解决的任务package.json

  • 它列出了项目所依赖的软件包(这是项目依赖关系的列表)。
  • 在其中,使用语义版本控制规则来设置项目可以使用的依赖程序包的版本。
  • 它使您可以重现程序包工作所需的环境,从而简化了将项目转移到其他开发人员的过程。

一个文件package.json可以被认为是一个README类固醇的文件在这里您可以描述程序包的依赖关系,在这里您可以编写在项目的组装和测试期间执行的脚本。同一文件包含有关由其开发人员指定的项目版本的信息以及对该项目的描述。我们package.json对指定项目依赖项的可能性特别感兴趣

该文件中指示了项目依赖关系的事实似乎有些令人震惊。假设有一个程序包依赖于另一个程序包,而另一个程序包则依赖于另一个程序包。这样的依赖关系链可以任意长。因此,安装唯一的软件包Gatsby.js意味着为项目配备了19,000个附加依赖项。

package.json中的依赖项类型


为了更好地了解项目依赖项列表随着时间的增长,让我们讨论项目可能具有的不同类型的依赖项。即,package.json可以找到以下描述各种依赖性的部分:

  • dependencies -这些是普通的依赖项,其功能在项目中使用,并且可以从其代码中访问。
  • devDependencies-这些是开发依赖项。例如,漂亮的用于格式化代码。
  • peerDependencies -如果将依赖项写入此部分,则软件包开发人员会通知安装该软件包的人他将需要此部分中指定的特定版本的软件包。
  • optionalDependencies -它们列出了可选的依赖项,例如无法安装,不会违反软件包的安装过程。
  • bundledDependencies — , . , NPM, , .

package-lock.json


我们都知道package-lock.json,在项目工作过程中,文件会不断发生变化。从其中删除了某些内容,向其中添加了一些内容。当查看包含此文件更新版本的PR时,这一点尤其明显。我们经常认为这是理所当然的。的文件被package-lock.json自动生成,每一个文件的时间package.json或文件夹的改变node_modules。这使您可以完全像安装项目依赖项时那样维护依赖项树的内容。在安装项目时,这允许重现依赖关系树。这解决了来自不同开发人员的同一软件包的不同版本的问题。

考虑一个依赖项包括React的项目。相应的条目位于package.json。如果你看文件package-lock.json 该项目的内容,那么您将看到类似以下内容的内容:

    "react": {
      "version": "16.13.0",
      "resolved": "https://registry.npmjs.org/react/-/react-16.13.0.tgz",
      "integrity": "sha512-TSavZz2iSLkq5/oiE7gnFzmURKZMltmi193rm5HEoUDAXpzT9Kzw6oNZnGoai/4+fUnm7FqS5dwgUL34TujcWQ==",
      "requires": {
        "loose-envify": "^1.1.0",
        "object-assign": "^4.1.1",
        "prop-types": "^15.6.2"
      }
    }

文件package-lock.json是项目依赖项的较大列表。这是依赖项版本,模块的路径(URI),用于验证模块和此模块所需软件包完整性的哈希值。如果阅读此文件,则可以找到React需要的所有软件包的记录。这就是依赖的真正地狱所在。这里描述了项目所需的一切。

了解Gatsby.js依赖项


仅建立了一个依赖关系,我们向项目添加多达19,000个依赖关系又是怎么回事?这都是关于依赖关系的。这就是为什么我们拥有我们拥有的:

$ npm install --save gatsby

...

+ gatsby@2.19.28
added 1 package from 1 contributor, removed 9 packages, updated 10 packages and audited 19001 packages in 40.382s

如果查看package.json,则只能找到一个依赖项。但是,如果您看一看package-lock.json,事实证明,摆在我们面前的是几乎14千字节的怪物。Gatsby.js存储库package-lock.json中的文件package.json,可以找到关于所有这些代码行含义的更详细的答案。有很多直接依赖项,即根据npm计算得出 132。如果每个依赖项都至少还有一个依赖项,那么项目依赖项的总数将加倍-并且将有264个依赖项。当然,在现实世界中并非如此。每个直接项目依赖项都具有多个固有依赖项。结果,项目依赖项列表很长。

例如,我们将关注lodash库用作其他软件包依赖项的次数

$ npm ls lodash
example-js-package@1.0.0
└─┬ gatsby@2.19.28
  ├─┬ @babel/core@7.8.6
  │ ├─┬ @babel/generator@7.8.6
  │ │ └── lodash@4.17.15  deduped
  │ ├─┬ @babel/types@7.8.6
  │ │ └── lodash@4.17.15  deduped
  │ └── lodash@4.17.15  deduped
  ├─┬ @babel/traverse@7.8.6
  │ └── lodash@4.17.15  deduped
  ├─┬ @typescript-eslint/parser@2.22.0
  │ └─┬ @typescript-eslint/typescript-estree@2.22.0
  │   └── lodash@4.17.15  deduped
  ├─┬ babel-preset-gatsby@0.2.29
  │ └─┬ @babel/preset-env@7.8.6
  │   ├─┬ @babel/plugin-transform-block-scoping@7.8.3
  │   │ └── lodash@4.17.15  deduped
  │   ├─┬ @babel/plugin-transform-classes@7.8.6
  │   │ └─┬ @babel/helper-define-map@7.8.3
  │   │   └── lodash@4.17.15  deduped
  │   ├─┬ @babel/plugin-transform-modules-amd@7.8.3
  │   │ └─┬ @babel/helper-module-transforms@7.8.6
  │   │   └── lodash@4.17.15  deduped
  │   └─┬ @babel/plugin-transform-sticky-regex@7.8.3
  │     └─┬ @babel/helper-regex@7.8.3
  │       └── lodash@4.17.15  deduped
  ...

幸运的是,大多数依赖项都由lodash的相同版本表示。使用这种方法,node_modules将只有一个lodash库文件夹。没错,通常情况并非如此。有时,不同的软件包需要同一软件包的不同版本。这就是为什么关于文件夹的巨大尺寸开了很多玩笑的原因node_modules但是,在我们的情况下,一切还不错:

$ du -sh node_modules
200M    node_modules

200兆还不错。我看到了该文件夹的大小如何轻松达到700 MB。如果您有兴趣了解哪些模块占用了最多的空间,可以运行以下命令:

$ du -sh ./node_modules/* | sort -nr | grep '\dM.*'
 17M    ./node_modules/rxjs
8.4M    ./node_modules/@types
7.4M    ./node_modules/core-js
6.8M    ./node_modules/@babel
5.4M    ./node_modules/gatsby
5.2M    ./node_modules/eslint
4.8M    ./node_modules/lodash
3.6M    ./node_modules/graphql-compose
3.6M    ./node_modules/@typescript-eslint
3.5M    ./node_modules/webpack
3.4M    ./node_modules/moment
3.3M    ./node_modules/webpack-dev-server
3.2M    ./node_modules/caniuse-lite
3.1M    ./node_modules/graphql
...

是的,rxjs是一个阴险的软件包。

这是一个简单的命令,可帮助减小文件夹的大小node_modules并简化其结构:

$ npm dedup
moved 1 package and audited 18701 packages in 4.622s

51 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

重复数据删除期间 npm尝试通过查找其他依赖项所使用的依赖项并将其移动以便可以共享来简化依赖项树的结构。这适用于我们的lodash示例。lodash @4.17.15因此,为了确保其可操作性,使用了许多软件包,仅安装一次此版本的库就足够了。当然,这是我们从一开始就只通过建立依赖关系就可以解决的情况。如果在处理项目的过程中package.json添加新的依赖项,建议有时记住团队npm dedup。如果使用纱线包管理器,则类似的命令看起来像纱线重复数据删除但是,实际上并不需要它,因为依赖优化是在执行命令时自动执行的yarn install

依赖可视化


对项目依赖关系的图形表示感兴趣吗?如果是这样,您可以使用特殊工具创建这样的演示文稿。让我们考虑其中的一些。

以下是使用npm.anvaka.com/获得的依赖项可视化结果


使用npm.anvaka.com进行依赖关系可视化

在这里您可以看到Gatsby.js项目包依赖关系的依赖关系。结果类似于庞大的网络。 Gatsby.js项目具有如此多的依赖关系,以至于这个“网络”几乎“悬挂”了我的浏览器。现在,如果有兴趣,请指向该图的链接。可以3D形式显示。

这是使用 npm.broofa.com制作的可视化文件


使用npm.broofa.com制作的依赖关系可视化的一部分

,类似于流程图。对于Gatsby.js来说,她非常复杂。你可以在这里看看可以根据 npms.io的估计为电路元素着色您可以将自己的文件上传到站点package.jsonPackage Phobia

工具可让您在安装软件包之前找出所需的空间。


使用软件包恐惧症收到的软件包信息

在这里,您可以找到已发布软件包的大小,以及安装后将占用多少磁盘空间。

底线:强大的力量带来巨大的责任


最后,我想说JavaScript和NPM是很棒的工具。好消息是,现代开发人员有机会使用大量依赖项。运行命令npm install以免编写几行代码非常容易,以至于有时我们忘记了后果。

到目前为止,您已经阅读了此书,您应该对npm-project依赖关系树结构的功能有更完整的了解。如果您向项目中添加一个很大的库,或者只是浏览项目的依赖项,则始终可以利用我们在此处讨论的内容并分析依赖项。

亲爱的读者们! 您是否要在npm项目中使用尽可能少的依赖项?


All Articles