网上很多文章介绍前端包/依赖管理的几个工具,我们这里从软件工程的角度,说一下他们的主要功能。
1. 软件工程
程序员实现某个功能,不会从零开始开发,都要基于大量第三方已经写好的功能模块,进行组装、扩展。
有了开源的协作力量,大量重复的有用的功能,被分成一个个单独的小模块(包),程序员之间可以进行共享,共同维护,测试、更新、添加更多新的功能。
常用编程语言的包管理工具:
- Java 语言有 Maven 仓库、Gradle;
- Go 语言有 dep 和 glide;
- Python 语言有 pip;
- Ruby 语言有 Gem 与 Bundler;
- PHP 语言有 (据说是最好的编程语言)Composer;
- Node.js 有多个包管理工具 NPM 仓库、Yarn、PNPM 等。
但是,论造轮子的能力,还是前端最强,
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 中央仓库有 Sonatype 维护:search.maven.org
- NPM 官方仓库:www.npmjs.com
- 镜像仓库
- 私有仓库
- Mavan 私有仓库可以用 Nexus 来搭建
- NPM 私有仓库可以用 verdaccio、nexus-npm 等来搭建
本地项目开发时,需要:
- 依赖配置文件(一般作为项目的入口文件)
- 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
- runnx
- executenu
- upgradenci
- clean installnrm
- remove
ni
相关的命令,都可以在末尾追加?
,表示只打印,不是真正执行,会输出真正要执行的命令- 假设项目目录下没有锁文件,默认就会让用户从
npm、yarn、pnpm
选择,然后执行相应的命令。但如果在~/.nirc
文件中设置了全局默认的配置,则使用默认配置执行对应命令