本文主要是记录 npm 实践中积累的一些经验,除了结合文档的理解,还补充了一些使用场景,主要涉及以下方面
-
指定安装源安装
-
通过 github 分支安装
-
包开发时如何本地调试?
-
多包管理
-
lerna
-
npm cli 工具安装后,如何让系统找到该命令?
-
你发布的 npm 包有哪些文件会被上传?
-
使用 npm 包会加载哪个文件入口?
-
如何让你的包尽量不制造冗余依赖?
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 中的这三个字段(优先级由低到高)
-
main:入口文件,browser 与 node 环境均可使用
-
module:ESM 规范的入口文件,browser 与 node 环境均可使用
-
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。
-
如果已经安装了同一个依赖,peerDependencies 指定的版本区间包含存在依赖的版本,那么peerDependencies 定义的不会再重复安装。
-
如果 peerDependencies 指定的版本区间不包含存在依赖的版本,还是会按照嵌套依赖树的策略下载安装,但是会报 warning,提示你自行修复,让版本兼容。
-
如果 peerDependencies 指定的包没有被安装过,会直接平铺安装该依赖。