本文参考:
前言
随着前端工程的发展,npm
已然成为每个前端开发者的必备技能,然而大多数人对它的使用也只是停留在使用npm
安装一些依赖包而已。作为全世界最大规模的包管理器,每周大约有30亿次的下载量,npm
的功能远不止安装依赖这么简单,本文的目的就是介绍npm
更多的功能。
tips
:本文 demo 仓库:npm-demo
1. 安装 npm 和管理 npm 版本
npm
是NodeJS自带的一个包管理工具,在 NodeJS官网 安装NodeJS
后即可使用npm
。
- 查看
npm
版本
$ npm -v
- 更新
npm
版本
$ npm install npm@latest -g
2. npm init
该命令的原理是调用脚本(init-package-json),输出一个初始化的package.json
文件,package.json
中记录了项目中的依赖包的信息(如名称、版本等)。
执行npm init
每次都会输入如上信息,可以使用npm init --yes
快速创建一个带有默认信息的package.json
的文件。
3. 安装依赖包
安装依赖包是npm
的核心功能,输入npm install
,npm
会自动从package.json
文件中寻找dependencies
,devDependencies
下的依赖包,并安装到项目的node-modules
文件夹下。
3.1 关于 package
当手动安装一个依赖包时,使用npm install <package>
命令即可安装,参数package
即是要安装的依赖包名,一般npm
会在 默认源仓库 中去查找对应的包名。然而,package
不光是一个包名,还有更多的定义方式,参见:Understanding Packages and Modules,以下是package
定义的规则:
- 包含
package.json
文件描述该程序的文件夹
- 满足规则(1)的
gzip
压缩文件
- 可以下载到规则(2)资源的
url
链接
- 格式为
<name>@<version>
,一般是已发布在npm源仓库
上的可访问url
,且该url
满足规则(3)
- 格式为
<name>@<tag>
,通过tag
标记获取到version
,且满足规则(4)
- 格式为
<name>
,默认添加latest
标记获取最新发布版,且满足规则(5)
- 满足规则(1)的
git url
(git url 支持格式)
按照以上不同规则安装后的package.json
文件如下:
注意:无论以何种方式安装依赖包,
npm install
时都会去根据package.json
文件中的dependencies
字段下载它所依赖的相关包
3.2 本地包应用场景
场景模拟如下所示:
- 创建一个公共模块
common-package-test
,存放项目中一些公共的配置或方法
- 在
src
目录下创建一个轮播图列表组件
- 利用
webpack
打包该文件,并进行测试
- 打开
index.html
,查看控制台,正常输出
通过以上场景模拟,内容可以正常输出,但是问题在于引用路径一直往上层寻找,这种引用很不利于项目的维护。而且公共模块也确实需要单独分离出来给其他模块引用,为了更优雅的引用,可以利用npm
本地包将公共模块封装到node_modules
中。
解决方案:
- 在公共模块
common-package-test
下创建一个package.json
文件
- 利用
npm
安装本地包
- 在轮播图组件重新引入
common-package-test
模块
- 通过 webpack 打包进行测试
3.3 远程 git 仓库包应用场景
场景:使用一个npm
包时发现它有bug,也找出了它的问题,并直接在node_modules
中修复了它,这样并没有用。因为在.gitignore
文件一般会忽略提交node_modules
,即node_modules
不在版本控制内,即使修复了它,下次下载依赖是还是会覆盖修复。
解决:先在git
仓库中fork
要修复的npm
包,然后在自己的仓库中修复该问题,最后在package.json
中引用自己的git url
即可解决问题。
4. 工作原理
-
npm2
采用的是递归安装的方法,当项目较复杂时,目录结构也会很深,会出现超出windows
系统中路径超过260字符的错误;部分相同的依赖包也会被重复安装,造成大量冗余。 -
npm3
采用扁平结构优化解决了以上问题,但也导致了npm
依赖树不能和文件结构所对应了,可以通过npm ls
来查看模块间的依赖关系。 -
npm5
依然采用扁平结构,还引入了package-lock.json
文件,它可以锁定依赖的安装结构和版本等信息。
package-lock.json
文件主要由version
,resolved
,integrity
,requires
,dependencies
几个字段组成。
-
version
:包的准确版本号 -
resolved
:安装源 -
integrity
:完整哈希 -
requires
:除最外层的requires
属性为true
, 其他的requires
属性都对应着该依赖包中的package.json
里记录的自己的依赖项 -
dependencies
:与node_modules
文件结构有着一一对应的关系。
使用package-lock.json
的好处在于可以锁定安装的依赖包,在另外一台设备上下载依赖会按照锁定版本去下载,这样就不会因为依赖包版本不同而导致一些意外的问题。
5. 依赖包版本管理
5.1 semver
-
npm
采用语义化版本 (semver) 规范进行版本管理。
版本格式:主版本号.次版本号.修订号
,版本号递增规则如下:
-
主版本号
:做了不兼容的 API 修改 -
次版本号
:做了向下兼容的功能性新增, -
修订号
:做了向下兼容的问题修正。
-
npm
提供了网站 npm semver calculator ,可以方便地计算semver
的匹配范围。下面列举一些规则示例:
-
^4.1.3
:指定的主版本号下,所有更新的版本 -
~4.1.3
:指定的次版本号下,所有更新的版本 -
>4.1.3
:版本号大于4.1.3
-
<=4.3
:版本号小于等于4.3
-
3.1.0 - 4.2.1
:版本号在3.1.0
到4.2.1
之间 -
>=3.3.1 <3.8.0
:与逻辑,版本号大于等于3.3.1
且小于3.8.0
-
>4.3.1 || <=2.3.1
:或逻辑,版本号大于4.3.1
或小于等于2.3.1
-
*
:所有主版本 -
2
或2.*
或2.x
:主版本号为2
的所有版本 -
3.3
或3.3.*
或3.3.x
:版本号为3.3
开头的所有版本 -
5.0.0-alpha
:预发布版本 -
7.0.0-beta.3
:预发布版本 -
1.0.0-rc.3
:预发布版本
5.2 依赖包管理建议
- 使用
npm5.2
以上的版本,保留package-lock.json
文件 - 不要手动修改
package-lock.json
- 升级小版本依赖包:
npm update <package>
- 升级大版本依赖包:
npm install <package>@<version>
- 降级依赖包:
npm install <package>@<version>
- 删除依赖包:
npm uninstall <package>
- 当提交了
package.json
,package-lock.json
的更新后,应及时拉取更新,并重新npm install
重新安装更新后的依赖
6. npm 脚本
6.1 npm scripts
- 使用
npm scripts
可以在package.json
文件中自定义脚本
{
"script": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
以上代码片段是create-react-app
脚手架生成的React
项目中的scripts
,可以使用npm run
命令来执行上面的scripts
,执行npm run build
就等同于执行对应的npm
脚本:react-scripts build
-
npm
脚本原理
当执行npm run
命令时,会自动新建一个shell
去执行里面的脚本,shell
会将./node_modules./bin
目录添加到PATH
变量中。也就是说,如果./node_modules./.bin
目录中的脚本,可以直接调用脚本名,不用去写完整的脚本路径或是全局安装脚本了。
- 简写
-
npm start
是npm run start
的简写 -
npm stop
是npm run stop
的简写 -
npm test
是npm run test
的简写 -
npm restart
是npm run stop && npm run restart && npm run start
的简写
- 变量
npm
脚本可以通过环境变量process.env
的方式获取到运行时的相关变量:
{
"name": "webpack",
"version": "3.8.1",
"repository": {
"type": "git",
"url": "https://github.com/webpack/webpack.git"
}
}
上面的代码片段是webpack
的package.json
文件中的部分内容,可以通过process.env
对象获取其中的变量。
例如:process.env.npm_package_name
返回webpack
,process.env.npm_package_version
返回3.8.1
,process.env.npm_package_repository_type
返回git
6.2 npx
- 先举个例子,在
npm5.2
版本以前,如果只是在项目中安装了webpack
,打包的时候就需要用./node_modules./.bin/webpack
来调用webpack
命令来进行打包。调用命令时每次都要输入这么长的路径,会觉得很麻烦,所以一般会在全局安装webpack
和webpack-cli
,然后直接用webpack
命令来打包,这是因为在webpack
的package.json
文件中有对bin
字段的定义:
"bin": {
"webpack": "./bin/webpack.js"
}
但是全局安装的webpack
命令可能会与项目使用的weboack
命令版本不同,也会导致不能成功构建。上一节讲到了npm scripts
,这时我们就可以将webpack
命令写到npm scripts
中,而不需要全局安装了,因为npm run
命令能够在执行时把./node_modules/.bin
加入到PATH
变量中,进而直接调用webpack
命令了
在npm5.2
版本以后,npx
很方便地解决了上述问题,直接使用npx webpack
就可以去自动寻找项目中安装好的webpack
依赖包,然后进行打包
- 执行远程
npm
远的二进制包
在npm5.2
版本以前,初始化React
脚手架项目,需要现在全局安装create-react-app
,然后再创建初始化的项目:
npm install -g create-react-app
create-react-app my-app
npm5.2
版本后,直接使用npx
即可安装并创建初始化项目,这样也就不必全局安装create-react-app
了,也不必去检查全局安装的依赖包版本是否过低:
npx create-react-app my-app
6.3 切换不同的Node
版本
在以前都是用 nvm 或 n 这样的Node
版本管理工具来切换Node
版本,而有了npx
同样可以在不手动切换Node
的前提下,使用不同的Node
版本环境:
7. npm 配置
7.1 npm-config
通过npm config ls -l
可以查看npm
的所有配置,详细的配置说明可在官方文档查看:config | npm Documentation
修改配置命令:npm config set <key> <value>
修改npm仓库源为淘宝镜像源:npm config set registry https://registry.npmjs.org/
7.2 .npmrc文件
上面的修改npm
配置是借助命令行来完成的,除了命令还可以通过.npmrc
文件来修改配置。
npm
读取config
配置的优先级:
- 工程目录下的
.npmrc
文件(/path/to/my/project/.npmrc
) - 用户配置文件(
~/.npmrc
) - 全局配置文件(
$PREFIX/etc/npmrc
) -
npm
内置的配置文件(/path/to/npm/npmrc
)
8. 总结
- 为了统一配置,项目中的
npm config
添加到.npmrc
文件中 - 统一
node
运行环境,package.json
和package-lock.json
文件 - 合理使用安装依赖包:
npm install <package>|<local file>|<git url>
- 使用
npm5.2
以上版本 - 合理使用
npm scripts
与npx
管理应用相关脚本
npm
的一些基础使用和介绍先到此为止,关于更加详细的使用和其他高级用法请看下一篇文章:npm总结(二)