本篇文章将讲述 npm
包的发展进化史,npm
与yarn
及pnpm
的区别,详解package.json
内容, npm
命令, npm实用技巧等内容。
npm使用的SemVer版本规范
在使用第三方依赖时,我们通常会在package.json中指定依赖的版本范围,语义化版本范围规定:
~
:只升级修订号^
:升级次版本号和修订号*
:升级到最新版本1.0.0-alpha.1
,1.0.0-beta.4
,2.0.0-rc.1
等。常用的先行版本一般为alpha
,beta
,rc
,stable
,csp
等。Alpha
: 是内部测试版,一般不向外部发布,会有很多Bug.Beta
: 该版本相对于α版已有了很大的改进,消除了严重的错误,但还是存在着一缺陷,需要经过多次测试来进一步消除。这个阶段的版本会一直加入新的功能。RC
:(Release Candidate) Candidate是候选人的意思,用在软件上就是候选版本。GA
:首次发行稳定的版本,也就是官方开始推荐广泛使用了Release
: 该版本意味“最终版本”关于前端包管理器推荐阅读 《前端包管理器探究》。这篇文章写的很好比较幽默也很全面,这里我做一些个人总结。
这种方式非常简单,但最大的问题是依赖地狱
Monorepo
项目的同学会深有体会), 在遇到版本冲突时才会在模块下的 node_modules 目录下存放该模块子依赖,虽然解决了依赖地狱问题,但也引入了新的问题
package.json
中定义, 但可以引用。 幽灵依赖的问题是 依赖不兼容 和 依赖缺失在npm v5中新增了
package-lock.json
。当项目有package.json
文件并首次执行npm install
安装后,会自动生成一个package-lock.json
文件,该文件里面记录了package.json
依赖的模块,以及模块的子依赖。并且给每个依赖标明了版本、获取地址和验证模块完整性哈希值。通过package-lock.json
,保障了依赖包安装的确定性与兼容性,使得每次安装都会出现相同的结果。
解决了依赖缺失和不确定性问题
Yarn
是在2016年开源的,yarn
的出现是为了解决 npm v3
中的存在的一些问题,那时 npm v5
还没发布。Yarn
被定义为快速、安全、可靠的依赖管理。
与npm一样,yarn也适用本地缓存,区别是yarn还提供了离线模式
Yarn v1 lockfile
yarn.lock
与 package-lock.json
的大体思路一致,具体区别如下
yarn
使用自定义格式 npm
使用json
格式package-lock.json
文件里记录的依赖的版本都是确定的, 不会出现范围符号。 而 yarn.lock
文件里仍然会出现语义化版本范围符号package-lock.json
文件内容更丰富,实现了更密集的锁文件,包括子依赖的提升信息
npm v5
只需要 package.lock
文件就可以确定 node_modules
目录结构yarn.lock
无法确定顶层依赖,需要 package.json
和 yarn.lock
两个文件才能确定 node_modules
目录结构。node_modules
目录中 package
的位置是在 yarn
的内部计算出来的,在使用不同版本的 yarn
时可能会引起不确定性。Yarn v2 NPN模式
pnp模式优缺点也非常明显:
pnpm1.0
诞生于2017年,它说它也是为了解决npm
和yarn
存在的问题😂 ,并声称具有安装速度快、节约磁盘空间、安全性好等优点
因为依赖结构本质上是有向无环图(DAG),但是npm和yarn通过文件目录和node resolve算法模拟的是有向无环图的一个超级(多了许多错误的祖先节点和兄弟节点之间的连接。pnpm也是通过硬链接和软链接结合的方式,更加精准的模拟DAG来解决yarn和npm的问题。
硬链接与软链接
ls source_file link_name
创建硬链接ln -s source_file link_name
创建软连接来看一下官方的一张图
这里就很好的解决了幽灵依赖问题
局限性
pnpm-lock.yaml
cnpm
是由阿里维护并开源的npm国内镜像源,支持官方npm registry 的镜像同步。tnpm
是在cnpm
基础之上,专为阿里巴巴经济体的同学服务,提供了私有的 npm
仓库,并沉淀了很多 Node.js
工程实践方案。npm | yarn | pnpm | |
---|---|---|---|
安装所有依赖 | npm install | yarn | pnpm install |
安装依赖 | npm install <pkg> | yarn add <pkg> | pnpm install <pkg> pnpm add <pkg> |
移除依赖 | npm uninstall <pkg> | yarn remove <pkg> | pnpm uninstall <pkg> pnpm remove <pkg> |
更新依赖 | npm update <pkg> | yarn update <pkg> | pnpm update <pkg> |
运行脚本 | npm run <script> | yarn run <script> | pnpm run <script> |
注: pnpm
没有全局依赖的概念(它已经做了全局缓存了)
执行npm install
安装常用的有两个参数
npm i vue | "vue": "^3.3.4" |
npm i vue@latest | "vue": "^3.3.4" |
npm i vue@2 | "vue": "^2.7.14" |
npm i vue@2.1 | "vue": "^2.1.10" |
npm i vue@2.1.2 | "vue": "^2.1.2" |
除了 ^
、*
、~
符号,
还支持 >
>=
<
<=
1.2.x
npm的镜像源管理工具
npm install -g nrm
nrm ls
# 查看可选的源nrm use [源名称]
# 切换源nrm add registry [你公司的npm私服地址]
# 增加源nrm del [源名称]
#删除对应的源nrm test [源名称]
#测试相应源的响应时间npm的版本管理
安装 以mac为例
vi .bash_profile
(在Users/youname
目录下) 增加插入这端的内容bashexport NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
nvm list
#查看node版本列表nvm install [版本号]
#安装某个nodejs版本nvm use [版本号] && npm alias default [版本号]
# 切换到某个版本nvm use system && nvm alias default system
开源私服 verdaccio
本地开发模块调试 假设你在开发B项目,有个组件你想封装成npm包C
npm link
npm link C
软链接 npm unlink B
npm run env
# 查看环境变量npm publish
命令 (可能需要npm login
)files
字段决定上传哪些内容bin
创建全局命令通常在开发脚手架需要用到这个type
指定.js
文件是CJS还是ESM规范,
commonjs
、module
前者是默认值.mjs
是 ESM规范,.cjs
是CJS规范,优先于type
字段main
指定包入口文件exports
main
字段module
字段 指定ESM模块的入口,如果你想开发一个包即支持EMS又支持CommonJS,就需要配置 module
和main
两个字段description
respository
autor
license
keywords
homepage
bugs
contributors
devDependencies
开发依赖 npm i XXX -D
dependencies
运行依赖 npm i XXX -S
npm i XXX
peerDependencies
如果你安装我,那么你最好也安装X,Y和Z. 参考optionalDependencies
可选的包bundledDependencies
你的包依赖一些第三方包,你担心某个或某几个三方包发生变更导致你的包也不能用了,那么就配置这个字段包,将这些第三方包和你的包绑定起来。overrides
package A
,package A
依赖B@0.1.0
版本,你知道依赖B存在问题,应该使用使用B@0.1.1
版本,则配置overrides:{"A":{"B":"0.1.1"}}
B@0.1.1
你期望任何地方都使用B@0.1.1
, 则配置overrides:{"B":"0.1.1"}
yarn
中 使用 resolutions
字段 具体用法点这里resolutions
,再有的 overrides
(npm8)engines
配置项目启动所需的node版本npm版本条件script
构建脚本 npm run XXX
执行脚本
precommit
通常配合lint-staged
使用,git commit
前进行检查prepush
git push
前进行检查,如执行单元测试lint-staged
字段version
包版本号 alpha
beta
rc
state
name
包名称config
通过 process.env.XXX
读取bin
非常重要,通常做一些脚手架包会用到如 vue-cli
egg-cli
,会在脚本头部插入#!/usr/bin/env node
表示执行node脚本,作为前端有很多人只会一些shell命令,对shell编程不太熟,可以用node进行编程操纵shell脚本require('child_process').exec
man
说明帮助, 值为字符串 或字符串数组private
不希望上传到npm上,用它publishConfig
发布相关的配置,非常重要,比如publishConfig.registry
字段就非常有用,能让你避免把包发不到其他私服(如cnpm私服,无需登录就能发包,比较恶心,不知道是不是故意这样设计的,利用别人的失误,窃取别人公司的代码)typing/types
TS解析文件入口,为用户提供更好的IDE支持lint-staged
通常配置eslint校验module
sideEffects
声明该模块是否含有副作用,为webpack TreeShake提供更大的压缩空间babel
eslint
style
、less
flat
yarn install --flat
将所有依赖项安装在项目的顶层 node_modules 目录下,而不是嵌套在子目录中。bowerserslist
目标环境(浏览器及node环境) 参考browser
指定浏览器环境的入口,以便在打包时可以根据目标环境选择不同的文件。参考资料
package-lock.json
就是锁定安装时的包的版本号,以保证其他人在npm install
时大家的依赖能保持一致。需要将该文件提交至git 官方是这样说的package-lock.json。(一开始我网上查到一般不需要将改文件提交,但有些例外情况比如使用了第三方包即将废弃的api。后来朋友提醒应该文件需要提交)
npm list/ls
npm la/ll
// 相当于npm ls --long
npm list/ls --depth=0
// 只列出父包依赖的模块npm list/ls <packageName>
npm view/info <packageName>
npm outdated
npm prune
# 整理项目中无关的模块npm [home | resp | bugs | docs] <packageName>
npm run dev --prefix /path/to/your/folder
#在不同的目录下运行脚本npm audit [--json]
# 安全漏洞检查,加上--json,以 JSON 格式生成漏洞报告npm audit fix
#自动修复安全漏洞npm config set save-prefix="~"
npm
默认安装模块时,会通过脱字符^
来限定所安装模块的主版本号。
可以配置npm
通过波浪符~
来限定安装模块版本号:
当然还可以配置npm
仅安装精确版本号的模块:npm config set save-exact true
项目版本号管理: 尽量使用 npm version 指令来自动更新version
npm version xxx -m "upgrade to %s for reasons" # %s
会自动替换为新版本号
运行的时候,会到node_modules/.bin
路径和环境变量$PATH
里面,检查命令是否存在。
由于 npx 会检查环境变量$PATH
,所以系统命令也可以调用。
./node_modules/.bin/babel src --out-dir lib
等价于 npx babel src --out-dir lib
npx
可以调用 ./node_modules/.bin/
下面的命令及系统命令
由于众所周知的问题,国内安装npm包可能失败,这里简单写一点笔记。
node-sass
是个坑,如遇到这方面的报错,请切换node版本或切换node-sass
版本 它与npm版本有个对应关系cnpm
、yarn
or pnpm
试试patch-package
这个包给node_modules
下面的文件打补丁 具体用法点这里bash# server.js
# console.log(process.args)
node server --port=3100
# 输出
#[
# '/Users/XXX/node',
# '/Users/XXX/node/server',
# '--port=3100'
#]
# 或者配置pkg.script字段 `"server": "node server"`
npm run server -- --port=3100
# 可以得到同样的结果
参考资料: npm script中命令行传参数
本文作者:郭敬文
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!