npm
npm 是 Node.js 标准的包管理工具, 随 Node.js 一同安装, 通过它可本地或全局安装依赖库并管理其版本。
npm 安装依赖
当使用 npm 安装软件包时, 可以执行两种安装类型:
- 本地安装
- 全局安装
默认情况下, 当输入 npm install <packageName>
命令时, 执行的是本地安装, 例如:
bash
npm install lodash
软件包会被安装到当前文件夹树中的 node_modules
子文件夹下。
并且, npm 还会在 package.json
文件的 dependencies
属性中添加 lodash
条目。
使用 -g
标志可以全局安装依赖:
bash
npm install lodash -g
-g
标志也可放在 package 之前:
bash
npm install -g lodash
全局安装时, npm 不会将软件包安装到本地文件夹下, 软件包确切位置可使用 npm root -g
查看。
bash
npm root -g
在 Windows 上, 可能是 C:\Users\YOU\AppData\Roaming\npm\node_modules
。 在 macOS 或 Linux 上, 此位置可能是 /usr/local/lib/node_modules
。
但是, 如果使用 nvm 来管理 Node.js 版本, 位置会有所不同。
例如, 软件包的位置可能为 /Users/joe/.nvm/versions/node/v8.9.0/lib/node_modules
。
适合全局安装的软件包
通常, 所有的软件包都应该本地安装。
这样可以确保计算机可以有数十个应用程序, 并且如果需要, 每个应用程序都可以运行不同的版本。
但是也并不包括所有, 下面是一些流行的全局软件包示例:
npm
vue-cli
create-react-app
nodemon
react-native-cli
grunt-cli
mocha
gatsby-cli
forever
可以通过在命令行上运行以下命令查看系统上已安装的全局安装包:
bash
npm list -g --depth 0
npm 依赖与开发依赖
当使用 npm install <package-name>
安装 npm 软件包时, 是将为其安装依赖项。
该软件包会被自动的列出在 package.json 文件中的 dependencies
列表下。
WARNING
在 npm 5 之前, 必须手动指定 --save
当添加了 -D
或 --save-dev
标志时, 则会将其安装为开发依赖项, 添加至 devDependencies
列表。
开发依赖是仅用于开发的软件包, 在生产环境并不需要, 如测试的软件包、webpack、Babel 等。
DANGER
-S
是 --save
的缩写, -D
是 --save-dev
的缩写
bash
npm install cowsay --save
# 等同于
npm install cowsay -S
npm install webpack --save-dev
# 等同于
npm install webpack -D
安装指定版本软件包
npm install <package-name>
会默认安装最新版本的软件包, 但有时候并不想如此。
可以使用 @
语法来安装指定版本的软件包
bash
npm install cowsay@1.3.0
如何使用或执行 npm 安装的软件包
当安装完软件包后, 如何使用它呢?
假设使用以下命令安装了流行的 JavaScript 实用工具库 lodash
:
bash
npm install lodash
这会把软件包安装到本地的 node_modules
文件夹中。
WARNING
若要在代码中使用它, 则只需要使用 require
将其导入到程序中:
js
const _ = require('lodash')
如果软件包是可执行文件, 该怎么办 ❓ ❓ ❓
在这种情况下, 他会把可执行文件放到 node_modules/.bin/
文件夹下。
可以利用 cowsay 来简单验证一下。
cowsay 软件包提供了一个命令行程序, 可以执行该程序以使母牛说些话 (以及其他动物也可以说话) 🐮 🐮 🐮 🐮 🐮 🐮
当使用 npm install cowsay
安装软件包时, 它会在 node_modules 文件夹中安装自身以及它的一些依赖包, 并有一个隐藏的 .bin
文件夹, 其中包含指向 cowsay 二进制文件的符号链接。
DANGER
那如何执行这些文件呢 ❓ ❓ ❓ 方式有三种:
- 相对路径执行
- npx 执行
- npm scripts 执行
可以输入 ./node_modules/.bin/packageName
来运行它, 例如:
bash
./node_modules/.bin/cowsay
但是 npm v5.2 版本起包含的 npx 才是更好的选择, 只需运行:
bash
npx cowsay
除此之外, 还可以在 package.json
文件中配置 npm scripts, 再执行 npm run scriptName
, 例如:
js
// package.json
{
"name": "cowsay-test",
"version": "1.0.0",
"scripts": {
"cowsay": "cowsay hello-cowsay"
},
"license": "ISC",
"dependencies": {
"cowsay": "^1.5.0"
}
}
bash
npm run cowsay
package.json 指南
package.json
文件是项目的清单。它可以做很多完全互不相关的事情。例如, 它是用于工具的配置中心。 它也是 npm
和 yarn
存储所有已安装软件包名称和版本的地方。
创建 package.json 文件的方式有两种:
- 手动创建
- 自动创建
手动创建即直接在项目根目录下新建一个 package.json
文件, 然后输入相关内容。
自动创建则是在根目录下执行 npm init
命令, 然后根据提示一步步输入相应内容后自动创建。
如使用默认配置, 可用 npm init -y
命令跳过配置自动创建。
json
{
"name": "default",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
这是一个更复杂的示例, 从 Vue.js 提取
json
{
"name": "test-project",
"version": "1.0.0",
"description": "A Vue.js project",
"main": "src/main.js",
"private": true,
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"unit": "jest --config test/unit/jest.conf.js --coverage",
"test": "npm run unit",
"lint": "eslint --ext .js,.vue src test/unit",
"build": "node build/build.js"
},
"dependencies": {
"vue": "^2.5.2"
},
"devDependencies": {
"autoprefixer": "^7.1.2",
"babel-core": "^6.22.1",
"babel-eslint": "^8.2.1",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-jest": "^21.0.2",
"babel-loader": "^7.1.1",
"babel-plugin-dynamic-import-node": "^1.2.0",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-plugin-transform-vue-jsx": "^3.5.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
"chalk": "^2.0.1",
"copy-webpack-plugin": "^4.0.1",
"css-loader": "^0.28.0",
"eslint": "^4.15.0",
"eslint-config-airbnb-base": "^11.3.0",
"eslint-friendly-formatter": "^3.0.0",
"eslint-import-resolver-webpack": "^0.8.3",
"eslint-loader": "^1.7.1",
"eslint-plugin-import": "^2.7.0",
"eslint-plugin-vue": "^4.0.0",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^1.1.4",
"friendly-errors-webpack-plugin": "^1.6.1",
"html-webpack-plugin": "^2.30.1",
"jest": "^22.0.4",
"jest-serializer-vue": "^0.3.0",
"node-notifier": "^5.1.2",
"optimize-css-assets-webpack-plugin": "^3.2.0",
"ora": "^1.2.0",
"portfinder": "^1.0.13",
"postcss-import": "^11.0.0",
"postcss-loader": "^2.0.8",
"postcss-url": "^7.2.1",
"rimraf": "^2.6.0",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"uglifyjs-webpack-plugin": "^1.1.1",
"url-loader": "^0.5.8",
"vue-jest": "^1.0.2",
"vue-loader": "^13.3.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.5.2",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^2.9.0",
"webpack-dev-server": "^2.9.1",
"webpack-merge": "^4.1.0"
},
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
},
"browserslist": ["> 1%", "last 2 versions", "not ie <= 8"]
}
package.json 属性详解
下面详细介绍可以使用的属性, 其中 *
代表重要:
name
* : 软件包名称, 名称必须少于 214 个字符, 且不能包含空格, 只能包含小写字母、连字符(-
)和下划线(_
)这是因为当软件包在
npm
上发布时, 它会基于此属性获得自己的 URL。如果在 Github 上公开的发布此软件包, 则 Github 仓库的名称是作为此属性的不错选择。
在使用
npm init -y
自动创建 package.json 文件时, 文件夹名称映射为name
属性, 所以文件夹名称须符合此规范。version
* : 软件包当前版本, 遵循语义版本控制记法, 这意味着始终以 3 个数字表示:x.y.z
第一个数字是主版本号, 第二个数字是次版本号, 第三个数字是补丁版本号。
仅修复缺陷的版本是补丁版本, 引入向后兼容的更改的版本是次版本, 具有重大更改的是主版本
author
* : 软件包作者, 可设置作者名, 邮箱与链接地址, 如authorName <email> (url)
示例:
json{ "author": "NodeJS中文网 <mail@nodejs.cn> (http://nodejs.cn)" }
也可使用以下格式
json{ "author": { "name": "NodeJS中文网", "email": "mail@nodejs.cn", "url": "http://nodejs.cn" } }
contributors
: 软件包其他贡献者示例:
json{ "contributors": ["NodeJS中文网 <mail@nodejs.cn> (http://nodejs.cn))"] }
也可使用以下格式:
json{ "contributors": [ { "name": "NodeJS中文网", "email": "mail@nodejs.cn", "url": "http://nodejs.cn" } ] }
bugs
: 链接到软件包的问题跟踪器, 最常用的是 Github 的 issue 页面示例:
json{ "bugs": "https://github.com/nodejscn/node-api-cn/issues" }
homepage
: 设置软件包的主页示例:
json{ "homepage": "http://nodejs.cn" }
description
: 软件包简短描述如果要将软件包发布到
npm
, 则这个属性特别有用, 人们可以知道软件包是干啥用的keywords
: 软件包关键字数组有助于人们在浏览相似的软件包或浏览 npmjs 网站时找到软件包
repository
: 软件包仓库所在位置示例
json"repository": "github:nodejscn/node-api-cn",
main
软件包入口点当应用程序导入此软件包时, 应用程序会在该位置搜素模块的导出
private
: 软件包是否私有如果设置为
true
, 可防止软件包被意外地发布到npm
上scripts
* : 定义一组可运行的 node 脚本WARNING
这些脚本是命令行应用程序,
可以通过
npm run xxx
或yarn xxx
来运行它们, 其中xxx
是命令名称, 如npm run dev
可以为命令使用任何名称, 脚本也可以是任何操作
json"scripts": { "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", "start": "npm run dev", "unit": "jest --config test/unit/jest.conf.js --coverage", "test": "npm run unit", "lint": "eslint --ext .js,.vue src test/unit", "build": "node build/build.js" }
dependencies
* : 生产依赖列表DANGER
当使用 npm 或 yarn 安装软件包时:
bashnpm install <PACKAGENAME> npm install --save <PACKAGENAME> yarn add <PACKAGENAME> yarn add --save <PACKAGENAME>
该软件包会自动地插入此列表中
devDependencies
* : 开发依赖列表DANGER
不同于
dependencies
, 这些软件包只需要安装在开发机器上, 而无需在生产环境中运行代码当使用 npm 或 yarn 安装软件包时:
bashnpm install --save-dev <PACKAGENAME> yarn add --dev <PACKAGENAME>
该软件包会自动地插入此列表
license
软件许可证, 让用户知道他们的使用权力和限制engines
: 软件包运行的 Node.js 或其他命令的版本示例
json"engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0", "yarn": "^0.13.0" }
browserslist
用于告知要支持哪些浏览器(及其版本)Babel、Autoprefixer 和其他工具会用到它, 以将所需的 polyfill 和 fallback 添加到目标浏览器
示例:
json"browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ]
此配置意味着需要支持使用率超过 1% (来自 CanIUse 的统计信息) 的所有浏览器的最新的 2 个主版本, 但不包含 IE8 及更低的版本
命令特有属性
package.json
文件还可以承载命令特有的配置, 例如 Babel、ESLint 等。
每个都有特有的属性, 例如 eslintConfig
、babel
等。它们是命令特有的, 可以在相应的命令/项目文档中找到如何使用它们。
npm 语义版本控制
如果 Node.js 软件包有一件很棒的事情, 那就是它们都同意使用语义版本控制作为版本编号。
语义版本控制的概念很简单:所有的版本都有 3 个数字 x.y.z
。
- 第一个数字是主版本
- 第二个数字是次版本
- 第三个数字是补丁版本
当发布新的版本时, 不仅仅是随心所欲地增加数字, 还要遵循以下规则:
- 当进行不兼容的 API 更改时, 则升级主版本
- 当以向后兼容的方式添加功能时, 则升级次版本
- 当进行向后兼容的缺陷修复时, 则升级补丁版本
该约定在所有的编程语言中均被采用, 每个 npm
软件包都必须遵守该规定, 这一点非常重要, 因为整个系统都依赖于此。
为什么这么重要 ❓ ❓ ❓
因为 npm
设置了一些规则, 可用于在 package.json
文件中选择要将软件包更新到的版本 (当运行 npm update
时)。
规则使用了这些符号:
^
* : 表示接受『主版本不变更即可』的更新。~
* : 只会更新补丁版本WARNING
如果写入的是
^0.13.0
, 只能更新补丁版本, 即0.13.1
可以, 但0.14.0
就不行。>
: 接受高于指定版本的任何版本>=
: 接受等于或高于指定版本的任何版本<
: 接受低于指定版本的任何版本=
: 接受确切的版本-
: 接受一定范围的版本。例如:2.1.0 - 2.6.2
||
: 组合集合。例如< 2.1 || > 2.6
无符号
* : 仅接受指定的特定版本 (如1.2.1
)latest
* : 使用可用的最新版本
package-lock.json
在版本 5 中, npm 引入了 package-lock.json
文件。
该文件旨在跟踪被安装的每个软件包的确切版本, 以便产品可以以相同的方式被 100% 复制 (即使依赖的软件包被更新)。
这解决了 package.json
一直尚未解决的特殊问题, 在 package.json 中, 可以使用 semver 表示法设置要升级到的版本 (补丁版本或次版本), 例如:
- 如果写入的是
〜0.13.0
,则只更新补丁版本:即0.13.1
可以,但0.14.0
不可以。 - 如果写入的是
^0.13.0
,则要更新补丁版本和次版本:即0.13.1
、0.14.0
、依此类推。 - 如果写入的是
0.13.0
,则始终使用确切的版本。
当 package.json 文件中所依赖的软件包指定了 ^
或 ~
语法时, 当执行 npm install
操作时, 可能会引入新的次版本或补丁版本。
即使补丁版本或次版本不应该引入重大的更改, 但是还是可能引入缺陷。
DANGER
package-lock.json
会固化当前安装的每个软件包的版本, 当运行 npm install
时, npm
会使用这些确切的版本。
当运行 npm update
时, package-lock.json
文件中的依赖的版本会被更新。
示例:
json
// package.json 代码片段
{
"dependencies": {
"cowsay": "^1.5.0"
}
}
json
// package-lock.json 代码片段
{
"dependencies": {
"cowsay": {
"version": "1.5.0",
"resolved": "https://registry.npmmirror.com/cowsay/-/cowsay-1.5.0.tgz",
"integrity": "sha512-8Ipzr54Z8zROr/62C8f0PdhQcDusS05gKTS87xxdji8VbWefWly0k8BwGK7+VqamOrkv3eGsCkPtvlHzrhWsCA==",
"requires": {
"get-stdin": "8.0.0",
"string-width": "~2.1.1",
"strip-final-newline": "2.0.0",
"yargs": "15.4.1"
}
}
}
}
可以看出, 在 package.json
文件中, cowsay 的版本是 ^1.5.0
, 而在 package-loak.json
中则固化了 1.5.0
DANGER
当存在 package-lock.json
文件时, npm instal
会选择此文件安装依赖。
当不存在 package-lock.json
文件时, npm instal
会选择 package.json
文件安装依赖。
在 npm 版本不低于 5 时, package-lock.json
文件会随着安装依赖自动生成或更新。
查看 npm 包安装的版本
若要查看所有已安装的 npm 软件包 (包括它们的依赖包) 的版本, 则:
bash
npm list
例如:
bash
❯ npm list
/Users/joe/dev/node/cowsay
└─┬ cowsay@1.3.1
├── get-stdin@5.0.1
├─┬ optimist@0.6.1
│ ├── minimist@0.0.10
│ └── wordwrap@0.0.3
├─┬ string-width@2.1.1
│ ├── is-fullwidth-code-point@2.0.0
│ └─┬ strip-ansi@4.0.0
│ └── ansi-regex@3.0.0
└── strip-eof@1.0.0
也可以打开 package-lock.json
文件, 但这需要一些视觉扫描。
npm list -g
也一样, 但适用于全局安装的软件包。
若仅要获取顶层的软件包 (基本就是 package.json
中列出的软件包), 则运行 npm list --depth=0
bash
❯ npm list --depth=0
/Users/joe/dev/node/cowsay
└── cowsay@1.3.1
也可以提供指定名称来获取特定软件包的版本:
bash
❯ npm list cowsay
/Users/joe/dev/node/cowsay
└── cowsay@1.3.1
若要查看软件包在 npm 仓库上最新的可用版本, 则运行 npm view [package_name] version
:
bash
❯ npm view cowsay version
1.5.0
若要查看 npm 包的所有版本, 则运行 npm view [package_name] versions
:
bash
❯ npm view cowsay versions
[
'1.0.0', '1.0.1', '1.0.2',
'1.0.3', '1.1.0', '1.1.1',
'1.1.2', '1.1.3', '1.1.4',
'1.1.5', '1.1.6', '1.1.7',
'1.1.8', '1.1.9', '1.2.0',
'1.2.1', '1.3.0', '1.3.1',
'1.4.0', '1.5.0'
]
npm 淘宝镜像
由于"墙"的存在, 咱们可能需要更换 npm 源才能正常下载和安装模块, 可以使用淘宝镜像。
临时使用:
bash
npm --registry https://registry.npm.taobao.org install <package-name>
永久使用:推荐
bash
npm config set registry https://registry.npm.taobao.org
验证是否成功:
bash
npm config get registry
恢复官方源:
bash
npm config set registry https://registry.npmjs.org
如果不想给 npm 设置代理, 也可以配置 cnpm, 这样一来 npm 依旧采用官方源。
bash
npm install -g cnpm --registry=https://registry.npm.taobao.org
WARNING
cnpm 安装软件包可能会多下载一些文件/文件夹, 建议还是给 npm 设置代理, 使用淘宝镜像。