npm 除了 install 还有啥?

812 阅读7分钟

1620896922675-8c77ef09-0ee3-487e-a977-77b8cab45d8a.png

本文主要是记录 npm 实践中积累的一些经验,除了结合文档的理解,还补充了一些使用场景,主要涉及以下方面

  1. 指定安装源安装

  2. 通过 github 分支安装

  3. 包开发时如何本地调试?

  4. 多包管理

  5. lerna

  6. npm cli 工具安装后,如何让系统找到该命令?

  7. 你发布的 npm 包有哪些文件会被上传?

  8. 使用 npm 包会加载哪个文件入口?

  9. 如何让你的包尽量不制造冗余依赖?

package 安装

对于 package 的常规安装方式大家都不陌生,除了可以使用 npm官方源,还有其它一些特别有用的安装方式。

指定安装源安装

常常有这样两种情况需要指定安装源,例如公司内部搭建了私有镜像,这时候就需要指定源来安装了;另外可能由于国内访问官方npm 速度较慢,使用第三方源来提高 package 安装速度。

通过指定 npm 命令的 --registry 参数来指定安装源

npm install xxx --registry=https://maven.xxx.com.cn/repository/npmjs/

通过 github 分支安装

在使用 github 开源库时,不可避免会出现开源库维护不及时,出现的 bug 影响线上体验,当你提交修复 PR 后也不能得到及时处理,因此为了快速修复线上问题,我们可以将 package 依赖临时切换到我们自己提交的 github 修复分支,等 PR 处理后再切回。

eg:安装 github ant-design 的 feature 分支

npm install ant-design/ant-design#feature

更多

可以通过 npm install -H 命令了解更多的安装方式。

npm install (with no args, in package dir)
npm install [<@scope>/]<pkg>
npm install [<@scope>/]<pkg>@<tag>
npm install [<@scope>/]<pkg>@<version>
npm install [<@scope>/]<pkg>@<version range>
npm install <alias>@npm:<name>
npm install <folder>
npm install <tarball file>
npm install <tarball url>
npm install <git:// url>
npm install <github username>/<github project>

package 开发

相信很多同学都有过 package 开发的经验。简单回顾一下流程

  • 注册 npmjs 账号

  • 使用 npm 脚手架命令 npm init ,完成 package 初始化

  • 开发代码并构建

  • 登录 npm login ,然后发布 npm publish

包开发时如何本地调试?

前端开发中,经常会对项目中通用逻辑抽取,发布单独的 npm 库,以便在其他项目互用。那么在开发中如何测试验证该库,每次修改都 npm publish 后并安装到业务应用再进行测试验证吗?显然这种方式非常低效。

通常我们会使用 npm link 软链接。例如我们从业务中抽取了 commonLogic 库,在业务应用中 link commonLogic 后,再对 commonLogic 修改,引用该库的业务应用的node_modules 中 commonLogic 会同步更新。

步骤:

1、将 commonLogic link 到全局

# 在commonLogic的根目录下执行,相当于 npm install commonLogic -g
npm link

2、在业务应用中创建 commonLogic 的软链接

# 在业务应用根目录下执行,从全局node_modules link commonLogic到业务应用中
npm link commonLogic

多包管理

包体积大小是一项重要的指标,像 react 等都按功能进行了拆包,让使用者按需安装以减小构建体积。因此当库到一定复杂程度,体积较大时就需要进行拆包处理了,也就是通常说的 mono-repo。

首先需要了解 workspaces 的概念,它是拆包后的子包集合,支持从根目录包中管理本地文件系统中的多个子包,在 npm install 阶段自动 link,不需要在本地文件系统对相互依赖的子包手动 link。如果启用 workspaces 需要设置 private 为 true,多包的根目录不需要对外发布,设置 private 能防止意外发布。

更为方便的多包管理工具

lerna 是一个优化多包管理流程的工具,能帮我们统一安装构建、清除子包依赖、运行所有子包单元测试等等,简化包管理流程。

lerna 常用的功能:

1、初始 lerna

运行 npx lerna init ,生成 lerna.json,可以自行对 lerna 进行配置

2、统一安装子包依赖

运行 lerna bootstrap 会为每个子包安装依赖,有依赖关系的子包,会由于 npm、yarn等包管理工具的 workspaces 特性自动 link。但根目录下 package.json 配置的依赖需要自己安装。

3、清除所有子包依赖

运行 lerna clean 会移除所有子包的 node_modules,同样的也不会移除根目录配置的依赖。

4、运行测试用例

lerna run test 会执行每个子包的 test 脚本,如果需要只执行某个子包的测试用例,可以运行类似 lerna run test --scope=@scope/minirepo 来指定子包。

npm cli 工具安装后,如何让系统找到该命令?

package.json 中可以配置 bin 字段,它定义了可执行文件的入口,使用 { 命令名:本地文件名 } 的 map 表示,如果全局安装该工具包,会将 bin 声明的文件软链接到 /usr/local/bin/xx-cli,但注意需要 bin 脚本引用的文件以 #!/usr/bin/env node 开头,确保脚本使用 node 执行启动。

你发布的 npm包有哪些文件会被上传?

package.json 中 files 字段是一个文件正则的数组,它用于描述包作为依赖被安装时的整个条目。它默认成为 ["*"],意味着它会包含所有文件。

有些文件不管你是否配置在 files 数组中,都会会被包括或排除,例如 .npmignore、node_modules 等文件会被排除,但 package.json、README 之类总是会被包括。

.npmignore 文件也会对其产生影响,该文件里定义的条目也会被排除,如果有 .gitignore 文件,没有 .npmignore 文件,.gitignore 文件的内容会代替 .npmignore 的内容。

使用npm 包会加载哪个文件入口?

我们常使用的模块规范有 ESM 与 commonjs,具体加载哪个文件入口跟你使用的模块规范、使用环境以及 npm 定义的入口有关,有个优先级。因此可以定义多种入口文件,以兼容不同环境、不同模块规范使用。

定义入口的就是package.json 中的这三个字段(优先级由低到高)

  1. main:入口文件,browser 与 node 环境均可使用

  2. module:ESM 规范的入口文件,browser 与 node 环境均可使用

  3. browser:browser 环境的入口文件

如何让你的包尽量不制造冗余依赖?

举个例子,ant-design 这类 react 组件库,肯定是需要依赖 react 的,那么 ant-design 依赖的 react 与你项目中的 react 版本不一致时,是否会出现问题呢?其实也不会出现啥大问题,只是会出现冗余依赖,导致构建的bundle 变大。

npm 早期依赖管理的策略是根据依赖嵌套结构来构建依赖树,导致依赖树的层级非常深,也下载了大量重复包资源。npm v3 后尽可能平铺依赖树,以便共享使用。

还是上面那个例子,如果 ant-design 使用 dependencies 定义,如果 react 版本不一致,由于已经有了其它版本的 react 存在,会继续使用早期嵌套依赖树的策略处理 ant-design 中的 react,会生成下面的结构。

 project
 └── node_modules
     ├── react@v1
     └── antd
         └── node_modules
             └── react@v2

事实上可以发现,ant-design 使用的 peerDependencies。

  1. 如果已经安装了同一个依赖,peerDependencies 指定的版本区间包含存在依赖的版本,那么peerDependencies 定义的不会再重复安装。

  2. 如果 peerDependencies 指定的版本区间不包含存在依赖的版本,还是会按照嵌套依赖树的策略下载安装,但是会报 warning,提示你自行修复,让版本兼容。

  3. 如果 peerDependencies 指定的包没有被安装过,会直接平铺安装该依赖。

1620905682571-2805eaa2-8afa-4735-9625-1beb957d775c.png