漫谈前端包管理工具

1,977 阅读4分钟

网上很多文章介绍前端包/依赖管理的几个工具,我们这里从软件工程的角度,说一下他们的主要功能。

1. 软件工程

程序员实现某个功能,不会从零开始开发,都要基于大量第三方已经写好的功能模块,进行组装、扩展。

有了开源的协作力量,大量重复的有用的功能,被分成一个个单独的小模块(包),程序员之间可以进行共享,共同维护,测试、更新、添加更多新的功能。

常用编程语言的包管理工具:

  • Java 语言有 Maven 仓库、Gradle;
  • Go 语言有 dep 和 glide;
  • Python 语言有 pip;
  • Ruby 语言有 Gem 与 Bundler;
  • PHP 语言有 (据说是最好的编程语言)Composer;
  • Node.js 有多个包管理工具 NPM 仓库YarnPNPM 等。

但是,论造轮子的能力,还是前端最强,

Atwood's Law:

Any application that can be written in JavaScript, will eventually be written in JavaScript.

任何可以用 JavaScript 来写的应用,最终都将用 JavaScript 来写。

其他语言有的,都会用 JS/TS 再实现一遍,其他人用 JS 写的,也会有多人多种方式实现,比如 jQuery 和 Zepto、Underscore 和 Lodash 等。

在编程语言之上,从应用的维度,也有包/依赖管理,那就是 Docker hub,在封装好的操作系统的基础上(Docker 容器),可以包含数据库、应用服务器等多种依赖,整体打包成 Docker 镜像。

2. 依赖管理的实现

首先要给每个包一个唯一的标识:

  • 分组
  • 包名
  • 语义版本控制(semver):主版本号.次版本号.补丁版本号
    • 主版本号: 当API发生改变,并与之前的版本不兼容的时候
    • 次版本号: 当增加了功能,但是向后兼容的时候
    • 补丁版本号:当做了向后兼容的缺陷修复的时候
    • 修饰符:^ ~ -latest alpha bate 等
  • 唯一值(hash 编码等)

然后有统一托管的地方:

本地项目开发时,需要:

  • 依赖配置文件(一般作为项目的入口文件)
    • Maven 采用 pom.xml,
    • Gradle 采用 build.gradle
    • PHP composer 采用 composer.json
    • Python pip 采用 requirements.txt
    • 前端项目(Node 或 web 项目等)采用 package.json
  • 本地缓存(用户 home 目录,系统根目录等)
  • 项目目录下缓存

3. 包管理的难点

  • 网络原因,访问国外官方镜像、或 Github 等第三方地址时,请求超时
  • 嵌套层数太深,A 依赖 B,B 依赖 C、C 依赖 D,安装时很耗时
  • 依赖冲突,A 依赖 B 的 1.0.0,而 C 依赖 B 的 2.0.0
  • 通配符,A 依赖 B 的 1.0.0,而 C 依赖 B 的 1.0.2,那么 A 和 C 都可以使用较新的 1.0.2 版本的 B
  • 磁盘空间占用大,之前 npm 多层嵌套后,目录路径可能会很长。对于 Windows 来说,有很多程序无法处理超过260个字符的文件路径名
node_modules
- package-A
-- node_modules
--- package-B
----- node_modules
------ package-C
-------- some-really-really-really-long-file-name-in-package-c.js

4. 前端包管理工具

下面说说几个前端包管理工具 npm、yarn、pnpm、ni 的不同

约定的修饰符:

  • ^(插入符号) 大版本下面的任意一个版本即可,第一位要相同,默认安装最新的版本。次版本号的变化并不会影响向后兼容性。因此,安装最新版的依赖库应该是能正常工作的,一般是的重要错误和安全方面的修复。
  • (波浪符号)次版本下面的任意一个版本即可,前两位要相同,默认安装最新的版本。

NPM 是 Node.js 安装时默认的包管理工具。NPM 团队做了很多的工作,以确保向后兼容,并在不同的环境中保持一致。(题外话:npmjs.com 被 GitHub 收购,而 GitHub 被微软收购)

Yarn 采取了不同的做法。每个 yarn 安装都会生成一个yarn.lock文件,解决由于语义版本控制而导致的npm安装的不确定性问题,而且它是默认创建的,lock 文件还包含要安装的内容的校验和,以确保使用的库的版本相同

pnpm 运行起来非常的快,超过了npm和yarn,采用了一种巧妙的方法,利用硬链接和符号链接来避免复制所有本地缓存源文件,这是 Yarn 最大的性能弱点之一。

ni 是在 npm、yarn 之上又封装了一层:

  • 根据锁文件猜测用哪个包管理器,假设你的项目中有锁文件 yarn.lock/pnpm-lock.yaml/package-lock.json,那么它最终会执行 相应的yarn install/pnpm i/npm i 命令。
  • 同时安装了其他命令:
    • nr - run
    • nx - execute
    • nu - upgrade
    • nci - clean install
    • nrm - remove
  • ni相关的命令,都可以在末尾追加?,表示只打印,不是真正执行,会输出真正要执行的命令
  • 假设项目目录下没有锁文件,默认就会让用户从npm、yarn、pnpm选择,然后执行相应的命令。但如果在~/.nirc文件中设置了全局默认的配置,则使用默认配置执行对应命令

参考