npm
npm
介绍
npm 全名 node package manager
基本命令
1 | # 查看 npm 命令列表 |
npm 使用
npm init一路回车,初始化package.json文件- 如果使用
-f(代表force)、-y(代表yes)直接跳过询问阶段,直接生成package.json文件
1 | npm init -y |
npm set 设置环境变量
mac的配置文件在vim ~/.npmrc*
1 | npm set init-author-name 'your name' |
上面命令等于为npm init设置了默认值,以后执行npm init的时候,package.json的作者姓名、邮件、主页、许可证字段就会自动写入预设的值。这些信息会存放在用户主目录的~/.npmrc文件,使得用户不用每个项目都输入
1 | # 全局设置 下载的每个模块都确切版本,而不是一个可选的版本范围,版本前不带 ^ 或 ~ |
1 | # 将指定的 $dir 目录,设为模块的全局安装目录,如果当前有这个目录的写权限,那么运行 npm i的时候,就不需要 sudo 命令授权了 |
1 | npm config set save-prefix ~ |
npm info
npm info 命令可以查看每个模块的具体信息
1 | # 查看underscore模块的信息 |
npm search
npm search 命令用于搜索 npm 仓库,它后面可以跟字符串,也可以跟正则表达式
1 | npm search <搜索> |
npm list
npm list 命令以树型结构列出当前项目安装的所有模块,以及它们依赖的模块
1 | npm list |
npm install
node 模块采用 npm install 命令安装
每个模块都可以全局安装或是本地安装,全局安装指的是将一个模块安装到系统目录中,各个项目都可以调用,一般来说,全局安装只适合于工具模块,比如 eslint 和 gulp,本地安装指的是将一个模块下载到当前项目的 node_modules 子目录,在项目目录之中,才能调用这个模块
1 | # 本地安装 |
安装不同版本
npm i 总是安装模块的最新版本,如果要安装模块的特定版本,可以在模块名后面加上特定的版本号
1 | npm i axios@latest # 安装最新版本 |
避免系统权限
默认情况下,npm 全局模块会安装在系统目录(/usr/local/lib),普通用户没有写入权限,需要使用 sudo,使用如下方式可以在没有 root 权限下,安装全局模块
首先,在主目录下新建配置文件 .npmrc,然后在改文件中将 prefix 变量定义到主目录下面
1 | prefix = /home/qingxin/npm |
然后在主目录下新建 npm 子目录
1 | mkdir ~/npm |
此后,全局安装的模块都会安装到这个子目录中,npm 也会到 ~/npm/bin 目录去寻找命令
最后,将这个路径在 .bash_profile 文件(或 .bashrc 文件)中加入 PATH 变量
1 | export PATH=~/npm/bin:$PATH |
npm update
npm update 命令用来更新本地安装的模块
1 | # 升级当前项目的指定模块 |
它会先到原创仓库查询最新版本,然后查询本地版本,如果本地版本不存在,或者远程版本较新,就会安装
使用 -S 或 --save 参数,可以在安装的时候更新 package.json 里面模块的版本号
注意,从 npm v2.6.1 开始,npm update 只更新顶层模块,而不更新依赖的依赖,以前版本是递归更新的,如果想取到老版本的效果,使用如下命令
1 | npm --depth 9999 update |
npm uninstall
npm uninstall 命令,卸载已安装的模块
1 | npm uninstall [package name] |
npm run
npm 不仅可以用来模块管理,还可以用来执行脚本,package.json 文件有一个 scripts 字段,可以用于指定脚本命令,供 npm 直接调用
npm run 命令会自动在环境变量 $PATH 添加 node_modules/.bin 目录,所以 scripts 字段里面调用命令时不用加上路径,这就避免了全局安装 NPM 模块
npm run 如果不加任何参数,直接运行,会列出 package.json 里面所有可以执行的脚本命令,npm run 实际上是 npm run lint 的缩写
npm 内置了两个命令简写,npm test 等同于执行 npm run test,npm start 等同于执行 npm run start
1 | npm run serve |
npm run 会创建一个 shell 执行指定的命令,并临时将 node_modules/.bin 加入 PATH 变量,这意味着本地模块可以直接运行
1 | npm i eslint -S |
例如运行了上面的命令,它产生了两个结果,eslint 被安装在当前目录的 node_modules 目录下,其次,node_modules/.bin 目录会生成一个符号链接 node_modules/.bin/eslint,指向 eslint 模块的可执行脚本,然后就可以在 package.json 的 script 属性里面,不带路径的引用 eslint 这个脚本
1 | { |
运行 npm run lint的时候,它会自动执行 ./node_modules/.bin/eslint .
如果希望一个操作的输出,是另一个操作的输入,可以借用 linux 系统的管道命令,酱两个操作链接在一起
1 | "build-js": "browserify browser/main.js | uglifyjs -mc > static/bundle.js" |
但是,更方便的写法是引用其他 npm run 命令
1 | "build": "npm run build-js && npm run build-css" |
上面的写法是先运行 npm run build-js,然后再运行 npm run build-css,两个命令中间用 && 连接。如果希望两个命令同时平行执行,它们中间可以用 & 连接
1 | // 一个例子 |
写在 scripts 属性中的命令,也可以在 node_modules/.bin 目录中直接写成 bash 脚本。下面是一个 bash 脚本
1 |
|
假定上面的脚本文件名为build.sh,并且权限为可执行,就可以在scripts属性中引用该文件
1 | "build-js": "bin/build.sh" |
npm run 参数
npm run 命令添加参数
1 | { |
上面代码指定 npm test,实际运行 mocha test/,如果要通过 npm test 命令,将参数传到 mocha,则参数之前要加上两个连词线
1 | npm run test -- anothertest.js |
上面命令表示,mocha 要运行所有 test 子目录的测试脚本,以及另外一个测试脚本anothertest.js。
npm run本身有一个参数 -s,表示关闭 npm 本身的输出,只输出脚本产生的结果
npm-run-all
npm-run-all这个模块可以运行多个scripts脚本命令
1 | npm i npm-run-all -D |
1 | # 继发执行 |
start脚本start脚本用于启动应用程序1
2
3
4"start": "npm-run-all --parallel dev serve"
# npm run start 并行执行 dev 脚本 和 serve 脚本命令
# 相当于
npm run dev & npm run serve如果没有
start脚本配置,npm start命令默认执行下面的脚本,前提是模块的根目录存在一个server.js文件1
node server.js
dev脚本命令
dev脚本命令,规定开发阶段所要做的处理,比如构建网页资源1
2
3
4
5
6
7
8
9
10
11
12
13
14"dev": "npm-run-all dev:*"
// 这个命令用于继发执行所有 dev 的子命令 例如:dev:sass dev:autoprefix
"predev:sass": "node-sass --source-map src/css/hoodie.css.map --output-style nested src/sass/base.scss src/css/hoodie.css"
// 这个命令 将 sass 文件编译为 css 文件,并生成 source map 文件 这个一个hook 后面会讲到
"dev:sass": "node-sass --source-map src/css/hoodie.css.map --watch --output-style nested src/sass/base.scss src/css/hoodie.css"
// 这个命令会监听 sass 文件的变动,只要有变动,就自动将其编译为css文件
"dev:autoprefix": "postcss --use autoprefixer --autoprefixer.browsers \"> 5%\" --output src/css/hoodie.css src/css/hoodie.css"
// 这个命令 为css文件加上浏览器前缀,限制条件是只考虑市场份额大于5%的浏览器
serve脚本命令serve脚本用于启动服务
1
2"serve": "live-server dist/ --port=9090"
// 上面命令启动服务,用的是live-server模块,将服务启动在9090端口,展示dist子目录live-server模块有三个功能- 启动一个
HTTP服务器,展示指定目录的index.html文件,通过该文件加载各种网络资源,这是file://协议做不到的 - 添加自动刷新功能。只要指定目录之中,文件有任何变化,它就会刷新页面
npm run serve命令执行以后,自动打开浏览器- 以前,上面三个功能需要三个模块来完成:
http-server、live-reload和opener,现在只要live-server一个模块就够了
- 启动一个
test脚本命令test命令用于执行测试
1
2
3
4"test": "npm-run-all test:*",
// 执行 npm run test 它会帮我们执行test的所有子命令 例如 test:lint
"test:lint": "sass-lint --verbose --config .sass-lint.yml src/sass/*"
// 上面命令规定,执行测试时,运行lint 脚本,检查脚本之中的语法错误prod脚本命令prod脚本命令,规定进入生产环境时需要做的处理- 例如下面的命令,将
sass文件转为css文件,并加上浏览器前缀
1
2
3"prod": "npm-run-all prod:*",
"prod:sass": "node-sass --output-style compressed src/sass/base.scss src/css/prod/hoodie.min.css",
"prod:autoprefix": "postcss --use autoprefixer --autoprefixer.browsers "> 5%" --output src/css/prod/hoodie.min.css src/css/prod/hoodie.min.css"help脚本命令help脚本命令用于展示帮助信息- 下面的命令,
markdown-chalk模块用于将指定的markdown文件,转为彩色文本显示在终端
1
"help": "markdown-chalk --input DEVLOPMENT.md"
docs脚本命令docs脚本用于生成文档- 下面的命令使用
kss-node模块,提供源码的注释生成markdown格式的文档。
1
"docs": "kss-node --source src/sass --homepage ../../styleguide.md"
pre- 和 post- 脚本
npm run 为每条命令提供了 pre- 和 post- 两个钩子(hook),以 npm run lint 为例,执行这条命令之前,npm 会先查看有没有定义 prelint 和 postlint 两个钩子,如果有的话,就先执行 npm run prelint,然后执行 npm run lint,最后执行 npm run postlint
1 | { |
注意:如果执行过程出错,就不会执行排在后面的脚本,即如果
prelint脚本执行出错,就不会接着执行lint和postlint脚本。一些常见的
pre-和post-脚本prepublish:发布一个模块前执行postpublish:发布一个模块后执行preinstall:用户执行npm install命令时,先执行该脚本postinstall:用户执行npm install命令时,安装结束后执行该脚本,通常用于将下载的源码编译成用户需要的格式,比如有些模块需要在用户机器上跟本地的C++模块一起编译preuninstall:卸载一个模块前执行postuninstall:卸载一个模块后执行preversion:更改模块版本前执行postversion:更改模块版本后执行pretest:运行npm test命令前执行posttest:运行npm test命令后执行prestop:运行npm stop命令前执行- poststop:运行
npm stop命令后执行 - prestart:运行
npm start命令前执行 - poststart:运行
npm start命令后执行 - prerestart:运行
npm restart命令前执行 - postrestart:运行
npm restart命令后执行
对于最后一个
npm restart命令,如果没有设置restart脚本,prerestart和postrestart会依次执行stop和start脚本另外,不能在
pre脚本之前再加pre,即prepretest脚本不起作用注意,即使
Npm可以自动运行pre和post脚本,也可以手动执行它们一些例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17{
"postinstall": "node lib/post_install.js"
// 用于处理从Git仓库拉下来的源码。比如,有些源码是用TypeScript写的,可能需要转换一下 安装之后执行 lib/post_install.js
}
{
"dist:modules": "babel ./src --out-dir ./dist-modules",
"gh-pages": "webpack",
"gh-pages:deploy": "gh-pages -d gh-pages",
"prepublish": "npm run dist:modules",
// npm run publish时,会先执行Babel编译,然后调用Webpack构建,最后发到Github Pages上面
"postpublish": "npm run gh-pages && npm run gh-pages:deploy"
// && 继发 先执行 npm run gh-page 然后执行 npm run gh-pages:deploy
}以上都是
npm相关操作的钩子,如果安装某些模块,还能支持Git相关的钩子。下面以husky模块为例1
npm install husky --save-dev
1
2
3
4
5
6
7
8
9
10{
"scripts": {
"lint": "eslint yourJsFiles.js",
"precommit": "npm run test && npm run lint",
// commit 之前
"prepush": "npm run test && npm run lint",
"...": "..."
// push之前
}
}类似作用的模块还有
pre-commit、precommit-hook等内部变量
scripts字段可以使用一些内部变量,主要是package.json的各种字段- 比如
package.json的内容是{"name":"foo", "version":"1.2.5"}那么变量npm_package_name的值就是foo,变量npm_package_version就是1.2.5
1
2
3
4
5
6{
"scripts":{
"bundle": "mkdir -p build/$npm_package_version/"
}
}
// 运行npm run bundle以后,将会生成build/1.2.5/子目录config字段也可以用来设置内部字段
1
2
3
4
5
6
7
8"name": "fooproject",
"config": {
"reporter": "xunit"
},
"scripts": {
"test": "mocha test/ --reporter $npm_package_config_reporter"
}
// 变量npm_package_config_reporter对应的就是reporter通配符
npm的通配符的规则如下*匹配0个或多个字符?匹配1个字符[...]匹配某个范围的字符。如果该范围的第一个字符是!或^,则匹配不在该范围的字符!(pattern|pattern|pattern)匹配任何不符合给定的模式?(pattern|pattern|pattern)匹配0个或1个给定的模式+(pattern|pattern|pattern)匹配1个或多个给定的模式*(a|b|c)匹配0个或多个给定的模式@(pattern|pat*|pat?erN)只匹配给定模式之一**如果出现在路径部分,表示0个或多个子目录
全局模块
- 全局模块可以直接在任何地方使用
npm link
就是将当前的目录临时放到全局下
开发 npm 模块的时候,有时我们会希望,边开发边试用,比如本地调试的时候 require('module'),会自动加载本机开发中的模块,node 规定,使用一个模块时,需要将其安装到全局或项目的 node_modules 目录中,对于开发中的模块,解决方法就是在全局的 node_modules 目录中,生成一个符号链接,指向模块的本地目录
npm link 就是起到这个作用,会自动创建这个符号链接
举个🌰
在项目根目录执行
1 | # 该命令会在 npm 全局模块目录中,生成一个符号链接文件,该文件的名字就是 package.json文件中指定的模块名 |
这个时候,已经可以全局调用模块了,但是如果我们要让这个模块安装在项目内,还要进行下面的步骤
切换到项目目录,再次运行 npm link 命令,并指定模块名
1 | src/myProject$ npm link myModule |
然后就可以加载模块了
1 | var moduleO = require("myModule") |
这样一来,myModule 的任何变化,都可以直接反映在 myProject 项目之中。但是,这样也出现了风险,任何在 myProject 目录中对myModule 的修改,都会反映到模块的源码中
如果你的项目不再需要该模块,可以在项目目录内使用 npm unlink 命令,删除符号链接
1 | src/myProject$ npm unlink myModule |
npm bin
1 | npm bin |
npm adduser
1 | npm adduser |
npm publish
npm publish 用于将当前模块发布到 npm 中,执行之前需要向 npm 申请用户名
1 | # 申请用户名 |
然后在 package.json 里写入 build 脚本
1 | "scripts": { |
运行上面的脚本,会将 source 目录中的 es6 源码文件,转为 distribution 目录里面的 es5 文件,然后创建 .npmignore 和 .gitignore ,分别写入以下内容
1 | # .npmignore |
npm deprecate
如果想要废弃某个版本的模块,使用 npm deprecate 命令
1 | npm deprecate my-thing@"< 0.2.3" "critical bug fixed in v0.2.3" |
运行上面的命令以后,小于 0.2.3 版本的模块的 package.json 都会写入一行警告,用户安装这些版本时,这行警告就会在命令行显示
npm owner
- 模块的维护者可以发布新版本,
npm owner命令用于管理模块的维护者
1 | # 列出指定模块的维护者 |
其他命令
npm home打开一个模块的主页npm repo打开一个模块的代码仓库1
2
3
4npm home $package
npm repo $package
# 这两个命令不需要提前安装模块npm outdated检查当前项目所依赖的模块,是否已经有新版本- 它会输出当前版本
current version应当安装的版本wanted version和最新发布的版本latest version
- 它会输出当前版本
npm prune检查当前项目的node_modules目录中,是否有package.json里面没有提到的模块,然后将所有这些模块输出在命令行npm shrinkwrap锁定当前项目的依赖模块的版本- 运行该命令后,会在当前项目的根目录下生成一个
npm-shrinkwrap.json文件,内容是node_modules目录下所有已经安装的模块,以及它们的精确版本 - 下次运行
npm install命令时,npm发现当前目录下有npm-shrinkwrap.json文件,就会只安装里面提到的模块,且版本也会保持一致
- 运行该命令后,会在当前项目的根目录下生成一个
git / npm
npm 版本号管理的问题
semver规范,规定了版本号,由MAJORMINORPATCHMAJOR可能不在兼容老版本MINOR新增了一些兼容旧版本的apiPATCH修复bug
git 的 tag 对应着 npm 的版本
1 | # 会自动和git进行关联 |
版本号的含义
2.2.0必须是2.2.0^2.2.0限定大版本,后面更新只要不超过2就可以~2.2.0限定前两个版本,后面的版本只要比0大就可以=2.0大于这个版本<=2.0小于等于这个版本
预发版本
alpha预览版本,内部测试版beta测试版,公开测试版rc最终测试版本
scripts
- 可以配置脚本的命令 快捷键(可以把很长的命令放到
scripts中) - 执行命令 会将当前的
node_modules目录下的.bin文件夹放到全局中(所以可以直接使用) npm run start可以简写为npm start
npx
npx和script一样可以帮我们直接运行.bin目录下的内容- 如果
.bin目录下存在 会执行对应脚本,如果不存在会下载运行
npx 只是一个临时的使用方案。 npm5.2 之后产生的
源的切换
1 | npm i nrm -g |



