🗒️使用 npm

来自官网 https://docs.npmjs.com/

npm 是一个 software registry(软件注册中心)。

npm 有三个组件:

  • website:发现 packages,设置 profiles(配置文件),管理 npm 的其它方面

  • command-line interface, CLI, 命令行界面:大多数开发者和 npm 的交互方式

  • registry:一个大型公共 database,关于 JavaScript 软件及其元信息

npm 可以:

  • 管理代码所需要的 packages

  • 下载可以立即使用的独立工具

  • 运行 package 而不用下载它,使用 npx

  • 管理代码及其依赖的多个版本

  • 管理代码的开发权限、创建组织和虚拟团队

  • ...

1. 入门

1.1 创建 account

创建一个 npm user account,以便在 public registry(公共注册表)上共享和下载 Javascript packages。

  1. npm login:在网页上注册成功之后,可以使用该命令测试登录下

    • 此时需要输入 username, password, email address

    • 若启用了 two-factor authentication,则还需要再输入一次性密码

  2. npm whoami:测试是否登录成功

1.2 安装 npm

使用 CLI 安装 npm。

安装 Node.js 时,会自动安装 npm。

然而考虑到 npm 的发布频率要高于 Node.js,所以建议安装 npm 的最新稳定版本,命令如下:

npm install npm@latest -g  # 最新的稳定版本

1.3 故障排除

详见 Getting started / Troubleshooting

2. packages 和 modules

public npm registry(注册表)是一个关于 JavaScript packages(包括软件和元数据)的 database。开发人员可以使用 npm registry 贡献 packages,这样其他人就能在自己的项目中下载和使用这些 packages 了。

npm registry 包含 packages,其中很多 packages 也是 Node modules 或包含 Node modules。

为了解析 package 的 name 和 version,npm 和一个 registry website(注册网站)进行对话,该网站实现了 CommonJS Package Registry 规范以读取 package 的信息。详见 npm CLI / Using npm / Registry:JavaScript Package Registry(注册表)。

2.1 packages

package 就是一个由 package.json 描述的文件或文件夹。package 必须包含 package.json 文件才能被发布到 npm registry(注册表)。

packages 可以是 unscoped(不限定范围的)也可以是 scoped(限定范围的)。scoped packages 可以是 private 也可以是 public。

package formats 可以是以下之一:

  1. 一个包含代码的文件夹,由 package.json 文件描述

  2. 包含 (1) 的 gzipped tarball(压缩包)

  3. 解析为 (2) 的 URL

  4. 使用 (3) 在 registry 上发布的 <name>@<version>

  5. 指向 (4) 的 <name>@<tag>

  6. 满足 (5) 的 <name>@latest

  7. 一个 git url,被 cloned 了之后就是 (1)

    • git URL 的格式可以是:

      • git://github.com/user/project.git#commit-ish

      • git+ssh://user@hostname:project.git#commit-ish

      • git+http://user@hostname/project/blah.git#commit-ish

      • git+https://user@hostname/project/blah.git#commit-ish

    • commit-ish 可以是任何能作为参数提供给 git checkout 的 tag, sha 或 branch。commit-ish 默认是 master

npm install

npm install <folder>
npm install <tarball file>
npm install <tarball url>

npm install [<@scope>/]<name>
npm install <alias>@npm:<name>
npm install [<@scope>/]<name>@<tag>
npm install [<@scope>/]<name>@<version>
npm install [<@scope>/]<name>@<version range>

npm install <git remote url>
npm install <githubname>/<githubrepo>[#<commit-ish>]
npm install github:<githubname>/<githubrepo>[#<commit-ish>]
npm install gist:[<githubname>/]<gistID>[#<commit-ish>|#semver:<semver>]
npm install bitbucket:<bitbucketname>/<bitbucketrepo>[#<commit-ish>]
npm install gitlab:<gitlabname>/<gitlabrepo>[#<commit-ish>]
# package name
npm
@npmcli/arborist         # @scope
@npmcli/arborist@latest  # @<tag>
npm@6.13.1               # @<version>
npm@^4.0.0

# alias
semver:@npm:@npmcli/semver-with-patch
semver:@npm:semver@7.2.2
semver:@npm:semver@legacy

# folder: local directory
./my-package
/opt/npm/my-package

# tarball
./my-package.tgz
https://registry.npmjs.org/semver/-/semver-1.0.0.tgz

# git urls
https://github.com/npm/cli.git   # full git url
git@github.com:npm/cli.git       # git shorthand
git+ssh://git@github.com/npm/cli#v6.0.0 
github:npm/cli#HEAD              # username/package
npm/cli#c12ea07

2.2 modules

module 是 node_modules 目录下的任何文件或目录,能通过 Node.js 的 require() 函数加载。

module 必须是以下之一:

  1. 一个文件夹,它包含 package.json 文件且里面有 main 字段

  2. 一个 JavaScript 文件

包含 package.json 文件的 modules 也是 packages。

在 Node 程序的上下文中,module 也是从文件加载的内容,比如以下代码,我们也会说变量 req 指向 request module。

var req = require('request')

2.3 scope

当注册一个 npm user account 或创建一个组织时,我们将被授予一个与之匹配的 scope。不同 scope 里的两个 package 可以重名。

package.json 文件中列出的依赖,scope 名就是 @/ 之间的内容。比如:

  • @npm/package-name

  • @npmcorp/package-name

npm packages 的可见性取决于它所在的 scope(namespace)以及为它设置的访问级别(private 或 public)。

public
private

org

✔️

✔️ 默认

user

✔️

✔️ 默认

unscoped

✔️ 始终

--

相关说明:

  1. 关于 private packages

    • private packages 始终是 scoped

    • scoped packages 默认是 private,如果想变 public 则在发布时要传一个命令行标识

  2. 关于 public packages(任何人都可以下载并使用)

    • unscoped packages 始终是 public

    • unscoped public packages 存在于 global public registry namespace

    • scoped public packages 属于某个用户或组织。当作为依赖包含在 package.json 里时必须以用户名或组织名开头

      • @username/package-name

      • @org-name/package-name

2.4 创建 package.json

详见《贡献 package》

3. 指定依赖项

package.json 文件中指定 dependenciesdevDependencies,当运行 npm install 时 npm 会下载它们。

  • dependencies 应用程序在 production 环境中需要的 packages

  • devDependencies 仅在 local development 和 testing 时需要的 packages

# 通过 CLI 添加
npm install <package-name> --save-prod # 默认选项
npm install <package-name> --save-dev

4. 版本和 tag

4.1 语义版本

semantic versioning,语义版本控制。

功能
类别
规则
示例

首次发布

New product

1.0.0 开始

1.0.0

向后兼容的 bug fixes

Patch release

增加第三位

1.0.1

向后兼容的 new features

Minor release

增加中间数字

并将最后的数字置零

1.1.0

破坏向后兼容的更改

Major release

增加第一个数字

并将中间+最后的数字置零

2.0.0

4.2 分发标签

dist-tags, distribution tags, 分发标签

dist-tags 是对语义版本控制的补充:更易于人类阅读,也能更有效地分发 packages。

由于 dist-tags 和语义版本共享一个命名空间,因此需要避免和现有版本号冲突,所以尽量不要使用以数字或者字母“v”开头的 dist-tags。

npm publish # 默认会打名为 latest 的 dist-tag
npm publish --tag <tag>
npm publish --tag beta

# 给特定 version 添加 dist-tag
npm dist-tag add <package-name>@<version> [<tag>]
npm dist-tag add example-package@1.4.0 stable

5. 从 registry 获取 packages

5.1 安装和下载

# locally 安装和下载
npm install <package_name>      # 默认是 locally 安装,默认会使用 'latest' tag
npm install <package_name>@beta # 指定 dist-tag
npm install @scope/package-name
npm install @scope/private-package-name

# globally 安装和下载
npm install -g <package_name>

npm install 命令会在当前目录中创建 node_modules 目录(如果尚不存在),并将 package 下载到该目录。如果本地(local)目录没有 package.json 文件,则 npm 会安装最新版本的 package,否则就会安装满足声明的 semantic versioning rule 的最新版本。

如果使用的是 npm 5.2+,推荐使用 npx 来 globally 运行 packages。全局安装 package 是让我们将 package code 作为本地计算机上的一组工具来使用。 Introducing npx: an npm package runner

相关阅读:全局安装时的 EACCES permissions errors

5.2 update

# 看哪些 packages 要更新
npm outdated
npm outdated -g --depth=0

# 更新
npm update                   # 更新 all 本地包
npm update -g                # 更新 all 全局包
npm update -g <package_name> # 更新 single 全局包

5.3 uninstall

uninstall 一个 package,npm 会完全删除和它相关的所有东西。

npm uninstall <package_name>
npm uninstall <@scope/package_name>

npm uninstall -g <package_name>
npm uninstall -g <@scope/package_name>

6. 依赖的安全漏洞

当使用 npm install 安装 package 时,npm audit 会自动运行。也可以手动运行 npm audit

npm audit  # npm 6.x.x

npm audit 会生成一份 audit reports(审计报告),里面包含了项目 dependencies 相关的 security vulnerabilities(安全漏洞),能帮我们修复漏洞或进一步排除故障。审计报告包含以下字段:

  1. severity:漏洞的严重程度

    • critical 立即解决

    • high 尽快解决

    • moderate 时间允许地解决

    • low 自己定

  2. description:关于漏洞的描述

  3. package:存在漏洞的包名

  4. patched in:漏洞被修复的版本

  5. dependency of:存在漏洞的包的依赖 module

  6. path:包含漏洞的代码路径

  7. more info:关于 security report 的链接

npm audit 会检查 direct dependencies, devDependencies, bundledDependencies 和 optionalDependencies,但不检查 peerDependencies

7. scripts

npm 如何处理 scripts 字段。

package.json 文件里的 scripts 属性支持多种 built-in scripts 及其 preset life cycle events,也支持任意 scripts。这些脚本可以通过 npm run-script <stage> 或简写的 npm run <stage> 来执行。来自 dependencies 的 scripts 可以通过 npm explore <pkg> -- npm run <stage> 来运行。

7.1 pre 和 post 脚本

名字以 prepost 开头即可。

{
  "scripts": {
    "precompress": "{{ executes BEFORE the `compress` script }}",
    "compress": "{{ run command to compress files }}",
    "postcompress": "{{ executes AFTER `compress` script }}"
  }
}

当运行命令 npm run compress 时会执行上面的那三个脚本。

7.2 life cycle 脚本

一些特殊的 life cycle scripts 只在特定的情况下发生。

  1. prepare

  2. prepublish

  3. prepublishOnly

  4. prepack

  5. postpack

  6. dependencies:只要 npm 命令导致 node_modules 目录发生更改,该脚本就会执行,它运行在更改(changes 以及 package.jsonpackage-lock.json 的文件更新)之后。

7.3 最佳实践

  1. 不要以 non-zero error code 退出

  2. 尽量不要使用 scripts 来做 npm 可以为我们做的事情

  3. 检查 env 以确定把东西放哪

  4. 不要在脚本命令前加上 "sudo"

    • 如果失败了,要用 root permissions,那就 sudo 有问题的 npm command

  5. 不要使用 install

  6. scripts 从 package folder 的 root 运行,不管当调用 npm 时的 current working directory 在哪里

    • 如果想让 script 基于某个子目录运行,可以使用 INIT_CWD 环境变量

Last updated