theboyaply
theboyaply
发布于 2022-06-24 / 682 阅读
0
0

node-npm 基础知识

http://nodejs.cn/learn

安装更新

安装

安装node直接去官网下载对应的.msi或其它包安装即可。

更新

windows下更新node,可以去官网下载需要更新的.msi版本,然后安装到原node路径即可。

更新npm|npx

  1. 在CMD命令行输入命令 npm install -g npm更新。
  2. 在CMD命令行输入命令npm install -g npm-windows-upgrade,然后使用npm-windows-upgrade更新。

从命令行运行node.js脚本

运行 Node.js 程序的常用方法是,运行全局可用的命令 node(安装 Node.js 之后)并且传入您要执行的文件的名称。

如果您的 Node.js 主应用程序文件是 app.js,则您可以通过键入以下来调用它:

node app.js

以上,你显式地告诉 shell 使用 node 运行你的脚本。

如何退出 Node.js 程序

有多种方法可以终止 Node.js 应用程序。

当在控制台中运行程序时,可以用 ctrl-C 关闭它,但我们这里要讨论的是以编程方式退出。

让我们从最极端的开始,看看为什么最好不要使用它。

process 核心模块提供了一种方便的方法,允许您以编程方式退出 Node.js 程序:process.exit()

当 Node.js 运行此行时,进程立即被强制终止。

这意味着任何待处理的回调、任何仍在发送的网络请求、任何文件系统访问、或者正在写入 stdoutstderr 的进程,所有这些都将立即被非正常地终止。

如果这对您来说没问题,您可以传入一个整数,向操作系统发出退出代码的信号:

process.exit(1)

默认的退出码为 0,表示成功。 不同的退出代码有不同的含义,您可能希望在自己的系统中使用它来让程序与其他程序通信。

您可以在 http://nodejs.cn/api/process.html#process_exit_codes 阅读更多关于退出代码的信息。

你也可以设置 process.exitCode 属性:

process.exitCode = 1

当程序结束时,Node.js 将返回该退出代码。

当所有处理完成后,程序将正常地退出。

很多时候我们使用 Node.js 启动服务器,比如这个 HTTP 服务器:

const express = require('express')
const app = express()

app.get('/', (req, res) => {
  res.send('Hi!')
})

app.listen(3000, () => console.log('Server ready'))

Express 是一个在底层使用 http 模块的框架,app.listen() 返回一个 http 实例。 如果您需要使用 HTTPS 为您的应用程序提供服务,则使用 https.createServer,因为 app.listen 仅使用 http 模块。

这个程序永远不会结束。 如果您调用 process.exit(),则任何当前待处理或正在运行的请求都将被中止。 这并不好。

在这种情况下,您需要向命令发送 SIGTERM 信号,并使用进程信号句柄处理它:

注意:process 不需要 “require”,它是自动可用的。

const express = require('express')

const app = express()

app.get('/', (req, res) => {
  res.send('Hi!')
})

const server = app.listen(3000, () => console.log('Server ready'))

process.on('SIGTERM', () => {
  server.close(() => {
    console.log('Process terminated')
  })
})

什么是信号?信号是一个 POSIX 互通系统:发送给进程的通知,以便通知它发生的事件。

SIGKILL 是告诉进程立即终止的信号,理想情况下会像 process.exit() 一样。

SIGTERM 是告诉进程正常终止的信号。 这是从 upstartsupervisord 等进程管理器发出的信号。

你可以从程序内部,在另一个函数中发送这个信号:

process.kill(process.pid, 'SIGTERM')

或者从另一个 Node.js 运行的程序、或者从您的系统中运行的任何其他应用程序(知道您要终止的进程的 PID)。

Node.js 从命令行接收参数

当使用以下命令调用 Node.js 应用程序时,可以传入任意数量的参数:

node app.js

参数可以是独立的,也可以具有键和值。

例如:

node app.js joe

node app.js name=joe

这会改变在 Node.js 代码中获取参数值的方式。

获取参数值的方法是使用 Node.js 中内置的 process 对象。

它公开了 argv 属性,该属性是一个包含所有命令行调用参数的数组。

第一个参数是 node 命令的完整路径。

第二个参数是正被执行的文件的完整路径。

所有其他的参数从第三个位置开始。

可以使用循环迭代所有的参数(包括 node 路径和文件路径):

process.argv.forEach((val, index) => {
  console.log(`${index}: ${val}`)
})

也可以通过创建一个排除了前两个参数的新数组来仅获取其他的参数:

const args = process.argv.slice(2)

如果参数没有索引名称,例如:

node app.js joe

则可以这样访问:

const args = process.argv.slice(2)
args[0]

如果是这种情况:

node app.js name=joe

args[0]name=joe,需要对其进行解析。 最好的方法是使用 minimist 库,该库有助于处理参数:

const args = require('minimist')(process.argv.slice(2))
args['name'] //joe

但是需要在每个参数名称之前使用双破折号:

node app.js --name=joe

node环境变量

Node.js 的 process 核心模块提供了 env 属性,该属性承载了在启动进程时设置的所有环境变量。

这是访问 NODE_ENV 环境变量的示例,该环境变量默认情况下被设置为 development

注意:process 不需要 “require”,它是自动可用的。

process.env.NODE_ENV // "development"

在脚本运行之前将其设置为 “production”,则可告诉 Node.js 这是生产环境。

可以用相同的方式访问设置的任何自定义的环境变量。

node开发环境与生产环境的区别

可以为生产环境和开发环境使用不同的配置。

Node.js 假定其始终运行在开发环境中。 可以通过设置 NODE_ENV=production 环境变量来向 Node.js 发出正在生产环境中运行的信号。

通常通过在 shell 中执行以下命令来完成:

export NODE_ENV=production

但最好将其放在的 shell 配置文件中(例如,使用 Bash shell 的 .bash_profile),否则当系统重启时,该设置不会被保留。

也可以通过将环境变量放在应用程序的初始化命令之前来应用它:

NODE_ENV=production node app.js

此环境变量是一个约定,在外部库中也广泛使用。

设置环境为 production 通常可以确保:

  • 日志记录保持在最低水平。
  • 更高的缓存级别以优化性能。

例如,如果 NODE_ENV 未被设置为 production,则 Express 使用的模板库 Pug 会在调试模式下进行编译。 Express 页面会在开发模式下按每个请求进行编译,而在生产环境中则会将其缓存。 还有更多的示例。

可以使用条件语句在不同的环境中执行代码:

if (process.env.NODE_ENV === "development") {
  //...
}
if (process.env.NODE_ENV === "production") {
  //...
}
if(['production', 'staging'].indexOf(process.env.NODE_ENV) >= 0) {
  //...
})

例如,在 Express 应用中,可以使用此工具为每个环境设置不同的错误处理程序:

if (process.env.NODE_ENV === "development") {
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }))
})

if (process.env.NODE_ENV === "production") {
  app.use(express.errorHandler())
})

node使用 exports 公开功能

Node.js 具有内置的模块系统。

Node.js 文件可以导入其他 Node.js 文件公开的功能。

当想要导入某些东西时,使用

const library = require('./library')

可以导入存在于当前文件夹中的 library.js 文件中公开的功能。

在此文件中,必须先公开功能,然后其他文件才能将其导入。

默认情况下,文件中定义的任何其他对象或变量都是私有的,不会公开给外界。

这就是 module 系统提供的 module.exports API 可以做的事。

当将对象或函数赋值为新的 exports 属性时,这就是要被公开的内容,因此,可以将其导入应用程序的其他部分或其他应用程序中。

可以通过两种方式进行操作。

第一种方式是将对象赋值给 module.exports(这是模块系统提供的对象),这会使文件只导出该对象:

const car = {  brand: 'Ford',  model: 'Fiesta'}
module.exports = car
//在另一个文件中
const car = require('./car')

第二种方式是将要导出的对象添加为 exports 的属性。这种方式可以导出多个对象、函数或数据:

const car = {  brand: 'Ford',  model: 'Fiesta'}
exports.car = car

或直接

exports.car = {
  brand: 'Ford',
  model: 'Fiesta'
}

在另一个文件中,则通过引用导入的属性来使用它:

const items = require('./items')
items.car

const car = require('./items').car

module.exportsexport 之间有什么区别?

前者公开了它指向的对象。 后者公开了它指向的对象的属性。

npm 包管理器

npm 是 Node.js 标准的软件包管理器。

Yarn 是 npm 的一个替代选择。

安装所有依赖

如果项目具有 package.json 文件,则通过运行:

npm install

它会在 node_modules 文件夹(如果尚不存在则会创建)中安装项目所需的所有东西。

安装单个软件包

也可以通过运行以下命令安装特定的软件包:

npm install <package-name>

通常会在此命令中看到更多标志:

  • --save 安装并添加条目到 package.json 文件的 dependencies。
  • --save-dev 安装并添加条目到 package.json 文件的 devDependencies。

区别主要是,devDependencies 通常是开发的工具(例如测试的库),而 dependencies 则是与生产环境中的应用程序相关。

更新软件包

通过运行以下命令,更新也变得很容易:

npm update

npm 会检查所有软件包是否有满足版本限制的更新版本。

也可以指定单个软件包进行更新:

npm update <package-name>

版本控制

除了简单的下载外,npm 还可以管理版本控制,因此可以指定软件包的任何特定版本,或者要求版本高于或低于所需版本。

很多时候,一个库仅与另一个库的主版本兼容。

或者,一个库的最新版本中有一个缺陷(仍未修复)引起了问题。

指定库的显式版本还有助于使每个人都使用相同的软件包版本,以便整个团队运行相同的版本,直至 package.json 文件被更新。

在所有这些情况中,版本控制都有很大的帮助,npm 遵循语义版本控制标准。

scripts运行任务

package.json 文件支持一种用于指定命令行任务(可通过使用以下方式运行)的格式:

npm run <task-name>

例如:

{
  "scripts": {
    "start-dev": "node lib/server-development",
    "start": "node lib/server-production"
  },
}

使用此特性运行 Webpack 是很常见的:

{
  "scripts": {
    "watch": "webpack --watch --progress --colors --config webpack.conf.js",
    "dev": "webpack --progress --colors --config webpack.conf.js",
    "prod": "NODE_ENV=production webpack -p --config webpack.conf.js",
  },
}

因此可以运行如下,而不是输入那些容易忘记或输入错误的长命令:

$ npm run watch
$ npm run dev
$ npm run prod

npm 将软件包安装到哪里

当使用 npm 安装软件包时,可以执行两种安装类型:

  • 本地安装
  • 全局安装

默认情况下,当输入 npm install 命令时,例如:

npm install lodash

软件包会被安装到当前文件树中的 node_modules 子文件夹下。

在这种情况下,npm 还会在当前文件夹中存在的 package.json 文件的 dependencies 属性中添加 lodash 条目。

使用 -g 标志可以执行全局安装:

npm install -g lodash

在这种情况下,npm 不会将软件包安装到本地文件夹下,而是使用全局的位置。

全局的位置到底在哪里?

npm root -g 命令会告知其在计算机上的确切位置。

在 macOS 或 Linux 上,此位置可能是 /usr/local/lib/node_modules。 在 Windows 上,可能是 C:\Users\YOU\AppData\Roaming\npm\node_modules

但是,如果使用 nvm 管理 Node.js 版本,则该位置会有所不同。

例如,使用 nvm,则软件包的位置可能为 /Users/joe/.nvm/versions/node/v8.9.0/lib/node_modules

如何使用或执行 npm 安装的软件包

当使用 npm 将软件包安装到 node_modules 文件夹中或全局安装时,如何在 Node.js 代码中使用它?

假设使用以下命令安装了流行的 JavaScript 实用工具库 lodash

npm install lodash

这会把软件包安装到本地的 node_modules 文件夹中。

若要在代码中使用它,则只需使用 require 将其导入到程序中:

const _ = require('lodash')

package.json

package.json 文件是项目的清单。 它可以做很多完全互不相关的事情。 例如,它是用于工具的配置中心。 它也是 npmyarn 存储所有已安装软件包的名称和版本的地方。

文件结构

这是一个示例的 package.json 文件:

{}

它是空的! 对于应用程序,package.json 文件中的内容没有固定的要求。 唯一的要求是必须遵守 JSON 格式,否则,尝试以编程的方式访问其属性的程序则无法读取它。

如果要构建要在 npm 上分发的 Node.js 软件包,则必须具有一组可帮助其他人使用它的属性。 稍后会详细介绍。

这是另一个 package.json:

{
  "name": "nodejs_cn"
}

它定义了 name 属性,用于告知应用程序或软件包的名称。

这是一个更复杂的示例,该示例是从 Vue.js 应用程序示例中提取的:

{
  "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"]
}

这里有很多东西:

  • version 表明了当前的版本。
  • name 设置了应用程序/软件包的名称。
  • description 是应用程序/软件包的简短描述。
  • main 设置了应用程序的入口点。
  • private 如果设置为 true,则可以防止应用程序/软件包被意外地发布到 npm
  • scripts 定义了一组可以运行的 node 脚本。
  • dependencies 设置了作为依赖安装的 npm 软件包的列表。
  • devDependencies 设置了作为开发依赖安装的 npm 软件包的列表。
  • engines 设置了此软件包/应用程序在哪个版本的 Node.js 上运行。
  • browserslist 用于告知要支持哪些浏览器(及其版本)。

以上所有的这些属性都可被 npm 或其他工具使用。

生成package.json文件

运行一下命令生成一个package.json

npm init

它会询问你一些相关的值,按照提示填写即可。

如果你想直接生成一个package.json,可以在后面添加--yes --y参数。

这是使用npm init --yes生成的一个初始文件:

{
  "name": "node",
  "version": "1.0.0",
  "description": "",
  "main": "progressBar.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

name

设置软件包的名称。

示例:

"name": "nodejs_cn"

名称必须少于 214 个字符,且不能包含空格,只能包含小写字母、连字符(-)或下划线(_)。

这是因为当软件包在 npm 上发布时,它会基于此属性获得自己的 URL。

如果在 GitHub 上公开地发布此软件包,则 GitHub 仓库的名称是作为此属性的不错选择。

author

列出软件包的作者名称。

示例:

{
  "author": "NodeJS中文网 <mail@nodejs.cn> (http://nodejs.cn)"
}

也可以使用以下格式:

{
  "author": {
    "name": "NodeJS中文网",
    "email": "mail@nodejs.cn",
    "url": "http://nodejs.cn"
  }
}

contributors

除作者外,该项目可以有一个或多个贡献者。 此属性是列出他们的数组。

示例:

{
  "contributors": ["NodeJS中文网 <mail@nodejs.cn> (http://nodejs.cn))"]
}

也可以使用以下格式:

{
  "contributors": [
    {
      "name": "NodeJS中文网",
      "email": "mail@nodejs.cn",
      "url": "http://nodejs.cn"
    }
  ]
}

bugs

链接到软件包的问题跟踪器,最常用的是 GitHub 的 issues 页面。

示例:

{
  "bugs": "https://github.com/nodejscn/node-api-cn/issues"
}

homepage

设置软件包的主页。

示例:

{
  "homepage": "http://nodejs.cn"
}

version

指定软件包的当前版本。

示例:

"version": "1.0.0"

此属性遵循版本的语义版本控制记法,这意味着版本始终以 3 个数字表示:x.x.x

第一个数字是主版本号,第二个数字是次版本号,第三个数字是补丁版本号。

这些数字中的含义是:仅修复缺陷的版本是补丁版本,引入向后兼容的更改的版本是次版本,具有重大更改的是主版本。

license

指定软件包的许可证。

示例:

"license": "MIT"

keywords

此属性包含与软件包功能相关的关键字数组。

示例:

"keywords": [
  "email",
  "machine learning",
  "ai"
]

这有助于人们在浏览相似的软件包或浏览 https://www.npmjs.com/ 网站时找到你的软件包。

description

此属性包含了对软件包的简短描述。

示例:

"description": "NodeJS中文网入门教程"

如果要将软件包发布到 npm,则这个属性特别有用,人们可以知道该软件包是干啥用的。

repository

此属性指定了此程序包仓库所在的位置。

示例:

"repository": "github:nodejscn/node-api-cn",

注意 github 前缀。 其他流行的服务商还包括:

"repository": "gitlab:nodejscn/node-api-cn",
"repository": "bitbucket:nodejscn/node-api-cn",

可以显式地设置版本控制系统:

"repository": {
  "type": "git",
  "url": "https://github.com/nodejscn/node-api-cn.git"
}

也可以使用其他的版本控制系统:

"repository": {
  "type": "svn",
  "url": "..."
}

main

设置软件包的入口点。

当在应用程序中导入此软件包时,应用程序会在该位置搜索模块的导出。

示例:

"main": "src/main.js"

private

如果设置为 true,则可以防止应用程序/软件包被意外发布到 npm 上。

示例:

"private": true

scripts

可以定义一组可以运行的 node 脚本。

示例:

"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"
}

这些脚本是命令行应用程序。 可以通过调用 npm run XXXXyarn XXXX 来运行它们,其中 XXXX 是命令的名称。 例如:npm run dev

可以为命令使用任何的名称,脚本也可以是任何操作。

dependencies

设置作为依赖安装的 npm 软件包的列表。

当使用 npm 或 yarn 安装软件包时:

npm install <PACKAGENAME>
yarn add <PACKAGENAME>

该软件包会被自动地插入此列表中。

示例:

"dependencies": {
  "vue": "^2.5.2"
}

devDependencies

设置作为开发依赖安装的 npm 软件包的列表。

它们不同于 dependencies,因为它们只需安装在开发机器上,而无需在生产环境中运行代码。

当使用 npm 或 yarn 安装软件包时:

npm install --save-dev <PACKAGENAME>
yarn add --dev <PACKAGENAME>

该软件包会被自动地插入此列表中。

示例:

"devDependencies": {
  "autoprefixer": "^7.1.2",
  "babel-core": "^6.22.1"
}

engines

设置此软件包/应用程序要运行的 Node.js 或其他命令的版本。

示例:

"engines": {
  "node": ">= 6.0.0",
  "npm": ">= 3.0.0",
  "yarn": "^0.13.0"
}

browserslist

用于告知要支持哪些浏览器(及其版本)。 Babel、Autoprefixer 和其他工具会用到它,以将所需的 polyfill 和 fallback 添加到目标浏览器。

示例:

"browserslist": [
  "> 1%",
  "last 2 versions",
  "not ie <= 8"
]

此配置意味着需要支持使用率超过 1%(来自 CanIUse.com 的统计信息)的所有浏览器的最新的 2 个主版本,但不含 IE8 及更低的版本。

(查看更多)

命令特有的属性

package.json 文件还可以承载命令特有的配置,例如 Babel、ESLint 等。

每个都有特有的属性,例如 eslintConfigbabel 等。 它们是命令特有的,可以在相应的命令/项目文档中找到如何使用它们。

软件包版本

在上面的描述中,已经看到类似以下的版本号:〜3.0.0^0.13.0。 它们是什么意思,还可以使用哪些其他的版本说明符?

该符号指定了软件包能从该依赖接受的更新。

鉴于使用了 semver(语义版本控制),所有的版本都有 3 个数字,第一个是主版本,第二个是次版本,第三个是补丁版本,详见规则

还可以在范围内组合以上大部分内容,例如:1.0.0 || >=1.1.0 <1.2.0,即使用 1.0.0 或从 1.1.0 开始但低于 1.2.0 的版本。

package-lock.json

在版本 5 中,npm 引入了 package-lock.json 文件。

那是什么?您可能知道 package.json 文件,它更常见并且存在的时间更长。

该文件旨在跟踪被安装的每个软件包的确切版本,以便产品可以以相同的方式被 100% 复制(即使软件包的维护者更新了软件包)。

这解决了 package.json 一直尚未解决的特殊问题。 在 package.json 中,可以使用 semver 表示法设置要升级到的版本(补丁版本或次版本),例如:

  • 如果写入的是 〜0.13.0,则只更新补丁版本:即 0.13.1 可以,但 0.14.0 不可以。
  • 如果写入的是 ^0.13.0,则要更新补丁版本和次版本:即 0.13.10.14.0、依此类推。
  • 如果写入的是 0.13.0,则始终使用确切的版本。

无需将 node_modules 文件夹(该文件夹通常很大)提交到 Git,当尝试使用 npm install 命令在另一台机器上复制项目时,如果指定了 语法并且软件包发布了补丁版本,则该软件包会被安装。 ^ 和次版本也一样。

如果指定确切的版本,例如示例中的 0.13.0,则不会受到此问题的影响。

可能是你,或者是其他人,会在某处尝试通过运行 npm install 初始化项目。

因此,原始的项目和新初始化的项目实际上是不同的。 即使补丁版本或次版本不应该引入重大的更改,但还是可能引入缺陷。

package-lock.json 会固化当前安装的每个软件包的版本,当运行 npm install时,npm 会使用这些确切的版本。

这个概念并不新鲜,其他编程语言的软件包管理器(例如 PHP 中的 Composer)使用类似的系统已有多年。

package-lock.json 文件需要被提交到 Git 仓库,以便被其他人获取(如果项目是公开的或有合作者,或者将 Git 作为部署源)。

当运行 npm update 时,package-lock.json 文件中的依赖的版本会被更新。

查看 npm 包安装的版本

若要查看所有已安装的 npm 软件包(包括它们的依赖包)的最新版本,则:

npm list

例如:

❯ 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 也一样,但适用于全局安装的软件包。

若要仅获取顶层的软件包(基本上就是告诉 npm 要安装并在 package.json 中列出的软件包),则运行 npm list --depth=0

❯ npm list --depth=0
/Users/joe/dev/node/cowsay
└── cowsay@1.3.1

也可以通过指定名称来获取特定软件包的版本:

❯ npm list cowsay
/Users/joe/dev/node/cowsay
└── cowsay@1.3.1

这也适用于安装的软件包的依赖:

❯ npm list minimist
/Users/joe/dev/node/cowsay
└─┬ cowsay@1.3.1
  └─┬ optimist@0.6.1
    └── minimist@0.0.10

如果要查看软件包在 npm 仓库上最新的可用版本,则运行 npm view [package_name] version

❯ npm view cowsay version
1.3.1

安装 npm 包的旧版本

可以使用 @ 语法来安装 npm 软件包的旧版本:

npm install <package>@<version>

示例:

npm install cowsay

以上命令会安装 1.3.1 版本(在撰写本文时)。

使用以下命令可以安装 1.2.0 版本:

npm install cowsay@1.2.0

全局的软件包也可以这样做:

npm install -g webpack@4.16.4

可能还有需要列出软件包所有的以前的版本。 可以使用 npm view <package> versions

❯ 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' ]

将所有 Node.js 依赖包更新到最新版本

当使用 npm install <packagename> 安装软件包时,该软件包最新的可用版本会被下载并放入 node_modules 文件夹中,并且还会将相应的条目添加到当前文件夹中存在的 package.jsonpackage-lock.json 文件中。

npm 会核计依赖,并安装这些依赖最新的可用版本。

假设要安装 cowsay,这是一个很酷的命令行工具,可以让母牛说话。

npm install cowsay 时,此条目会被添加到 package.json 文件中:

{
  "dependencies": {
    "cowsay": "^1.3.1"
  }
}

以下是 package-lock.json 的片段,为方便查看,在其中删除了嵌套的依赖:

{
  "requires": true,
  "lockfileVersion": 1,
  "dependencies": {
    "cowsay": {
      "version": "1.3.1",
      "resolved": "https://registry.npmjs.org/cowsay/-/cowsay-1.3.1.tgz",
      "integrity": "sha512-3PVFe6FePVtPj1HTeLin9v8WyLl+VmM1l1H/5P+BTTDkMAjufp+0F9eLjzRnOHzVAYeIYFF5po5NjRrgefnRMQ==",
      "requires": {
        "get-stdin": "^5.0.1",
        "optimist": "~0.6.1",
        "string-width": "~2.1.1",
        "strip-eof": "^1.0.0"
      }
    }
  }
}

现在,这两个文件告诉我们,已安装了 cowsay 的 1.3.1 版本,并且更新的规则是 ^1.3.1(这对于 npm 版本控制规则意味着 npm 可以更新到补丁版本和次版本:即 1.3.21.4.0、依此类推)。

如果有新的次版本或补丁版本,并且输入了 npm update,则已安装的版本会被更新,并且 package-lock.json 文件会被新版本填充。

package.json 则保持不变。

若要发觉软件包的新版本,则运行 npm outdated

以下是一个仓库中一些过时的软件包的列表,该仓库已很长时间没有更新:

过时的软件包

这些更新中有些是主版本。 运行 npm update 不会更新那些版本。 主版本永远不会被这种方式更新,因为它们(根据定义)会引入重大的更改,npm 希望为你减少麻烦。

若要将所有软件包更新到新的主版本,则全局地安装 npm-check-updates 软件包:

npm install -g npm-check-updates

然后运行:

ncu -u

这会升级 package.json 文件的 dependenciesdevDependencies 中的所有版本,以便 npm 可以安装新的主版本。

现在可以运行更新了:

npm update

如果只是下载了项目还没有 node_modules 依赖包,并且想先安装新的版本,则运行:

npm install

使用 npm 的语义版本控制

如果 Node.js 软件包中有一件很棒的事情,那就是它们都同意使用语义版本控制作为版本编号。

语义版本控制的概念很简单:所有的版本都有 3 个数字:x.y.z

  • 第一个数字是主版本。
  • 第二个数字是次版本。
  • 第三个数字是补丁版本。

当发布新的版本时,不仅仅是随心所欲地增加数字,还要遵循以下规则:

  • 当进行不兼容的 API 更改时,则升级主版本。
  • 当以向后兼容的方式添加功能时,则升级次版本。
  • 当进行向后兼容的缺陷修复时,则升级补丁版本。

该约定在所有编程语言中均被采用,每个 npm 软件包都必须遵守该约定,这一点非常重要,因为整个系统都依赖于此。

为什么这么重要?

因为 npm 设置了一些规则,可用于在 package.json 文件中选择要将软件包更新到的版本(当运行 npm update 时)。

规则使用了这些符号:

  • ^
  • ~
  • >
  • >=
  • <
  • <=
  • =
  • -
  • ||

这些规则的详情如下:

  • ^: 只会执行不更改最左边非零数字的更新。 如果写入的是 ^0.13.0,则当运行 npm update 时,可以更新到 0.13.10.13.2 等,但不能更新到 0.14.0 或更高版本。 如果写入的是 ^1.13.0,则当运行 npm update 时,可以更新到 1.13.11.14.0 等,但不能更新到 2.0.0 或更高版本。
  • ~: 如果写入的是 〜0.13.0,则当运行 npm update 时,会更新到补丁版本:即 0.13.1 可以,但 0.14.0 不可以。
  • >: 接受高于指定版本的任何版本。
  • >=: 接受等于或高于指定版本的任何版本。
  • <=: 接受等于或低于指定版本的任何版本。
  • <: 接受低于指定版本的任何版本。
  • =: 接受确切的版本。
  • -: 接受一定范围的版本。例如:2.1.0 - 2.6.2
  • ||: 组合集合。例如 < 2.1 || > 2.6

可以合并其中的一些符号,例如 1.0.0 || >=1.1.0 <1.2.0,即使用 1.0.0 或从 1.1.0 开始但低于 1.2.0 的版本。

还有其他的规则:

  • 无符号: 仅接受指定的特定版本(例如 1.2.1)。
  • latest: 使用可用的最新版本。

卸载 npm 软件包

若要卸载之前在本地安装(在 node_modules 文件夹使用 npm install <package-name>)的软件包,则从项目的根文件夹(包含 node_modules 文件夹的文件夹)中运行:

npm uninstall <package-name>

如果使用 -S--save 标志,则此操作还会移除 package.json 文件中的引用。

如果程序包是开发依赖项(列出在 package.json 文件的 devDependencies 中),则必须使用 -D--save-dev 标志从文件中移除:

npm uninstall -S <package-name>
npm uninstall -D <package-name>

如果该软件包是全局安装的,则需要添加 -g--global 标志:

npm uninstall -g <package-name>

例如:

npm uninstall -g webpack

可以在系统上的任何位置运行此命令,因为当前所在的文件夹无关紧要。

npm 全局或本地的软件包

本地和全局的软件包之间的主要区别是:

  • 本地的软件包 安装在运行 npm install <package-name> 的目录中,并且放置在此目录下的 node_modules 文件夹中。
  • 全局的软件包 放在系统中的单独位置(确切的位置取决于设置),无论在何处运行 npm install -g <package-name>

在代码中,应该只引入本地的软件包:

require('package-name')

所以何时应该以一种或另一种方式安装?

通常,所有的软件包都应本地安装。

这样可以确保计算机中可以有数十个应用程序,并且如果需要,每个应用程序都可以运行不同的版本。

更新全局软件包会使所有的项目都使用新的版本,这可能会导致维护方面的噩梦,因为某些软件包可能会破坏与其他依赖项的兼容性等。

所有的项目都有自己的软件包本地版本,即使这看起来有点浪费资源,但与可能产生的负面影响相比也很小。

当程序包提供了可从 shell(CLI)运行的可执行命令、且可在项目间复用时,则该程序包应被全局安装。

也可以在本地安装可执行命令并使用 npx 运行,但是某些软件包最好在全局安装。

一些流行的全局软件包的示例有:

  • npm
  • create-react-app
  • vue-cli
  • grunt-cli
  • mocha
  • react-native-cli
  • gatsby-cli
  • forever
  • nodemon

可能已经在系统上安装了一些全局软件包。 可以通过在命令行上运行以下命令查看:

npm list -g --depth 0

npm 依赖与开发依赖

当使用 npm install <package-name> 安装 npm 软件包时,是将其安装为依赖项。

该软件包会被自动地列出在 package.json 文件中的 dependencies 列表下(在 npm 5 之前:必须手动指定 --save)。

当添加了 -D--save-dev 标志时,则会将其安装为开发依赖项(会被添加到 devDependencies 列表)。

开发依赖是仅用于开发的程序包,在生产环境中并不需要。 例如测试的软件包、webpack 或 Babel。

当投入生产环境时,如果输入 npm install 且该文件夹包含 package.json 文件时,则会安装它们,因为 npm 会假定这是开发部署。

需要设置 --production 标志(npm install --production),以避免安装这些开发依赖项。

如何创建 Node.js 模块

Node.js 模块是一种可以发布到 npm 的包。当你创建一个新模块时,创建 package.json 文件是第一步。

你可以使用 npm init 命令创建 package.json 文件。命令行中将会提示 package.json 字段中需要你输入的值。名称(name)版本(version) 这两个字段是必填的。你还需要输入 入口文件字段(main) 字段,当然,可以使用默认值 index.js。这些可以看上面的package.json文件描述。

我们在入口文件中添加2个函数,作为 exports对象的2个属性。这样,require 此文件之后,这2个函数在其他代码中就可以使用了。

exports.printMsg = function() {
  console.log("This is a message from the demo package");
}

exports.sayHi = function(name = "handsome guy") {
  console.log("Hi " + name);
}

试一试:

  1. 将你的包发布到 npm。
  2. 在你的项目外新建一个目录。
  3. 然后 cd 进入这个新目录。
  4. 执行 npm install <package> 命令。
  5. 创建一个 test.js 文件,require 这个包,并调用其中的方法。
  6. 执行 node test.js 命令。是否输出 console.log 中填写的信息?

发布npm包

创建/登录账号

要发布npm包,你必须在https://www.npmjs.com站点创建一个账号。

如果你没有账号,可以在终端使用npm adduser创建。也可以去站点上创建,然后在终端使用npm login进行登录。

登录之后在终端使用npm whoami查看是否登录(从技术上讲,这也意味着你的凭据已存储在本地)。

检查npm包目录

1、检查内容

一般来讲,目录下的所有内容都会被推送到远程,除非添加了.gitignore.npmignore文件。要了解如何使用这些命令,请参阅npm-developers

2、检查package.json文件

文件必须包含nameversion属性。

为包选择唯一的name,请尝试选择一个描述性名称:

  • 没有被别人使用
  • 与其他名称不同,没有拼写错误
  • 不会混淆他人的作者身份
  • 遵循 npm policy guidelines。例如,不要将您的包命名为冒犯性的名称,也不要使用其他人的商标名
  • package.json文件的相应行中指定名称

3、包含一个readme.md文件

当别人在npm站点浏览你的包时,这个文件的内容会展示在站点中。

推送npm包

直接使用npm publish推送这个包。

检查是否推送成功

去站点搜索你的包名,或者直接访问https://npmjs.com/package/

更新npm包

当你做出一些变动的时候,可以使用npm version <update_type>,这个将会直接更改package.json中的版本号。

其中,<update_type>是语义版本控制发布类型之一,补丁、次要或主要,你可以在查看。

Note: this will also add a tag with the updated release number to your git repository if you have linked one to your npm account.

更新版本号后,再次运行npm publish即可。

更新readme文件

The README displayed on the site will not be updated unless a new version of your package is published, so you need to run npm version patch and npm publish to update the documentation displayed on the site.

发布npm scope包

什么是scope(作用域)包?如果你熟悉vue的话,那么你可能会经常看到以下依赖:

{
    "dependencies": {
        "vue": "2.6.12"
    },
    "devDependencies": {
        "@vue/cli-plugin-babel": "4.4.6",
    	"@vue/cli-plugin-eslint": "4.4.6",
        "@vue/cli-service": "4.4.6"
    }
}

上面@vue开头的就属于作用域包,作用域用于将相关包分组在一起,并为npm模块创建名称空间(如域)。更多信息看这里 here.

如果一个包的开头是@,那么它就是一个作用域包,其内容是@/之间的所有字符:

@scope/project-name

每一个npm用户都有一个自己的作用域:

@username/project-name

创建一个作用域包,你只需要把package.jsonname改成符合作用域包名称的规则即可。

{
  "name": "@username/project-name"
}

如果你使用npm init,你可以添加--scope=username参数:

npm init --scope=username

默认情况下,作用域包是私有的。要发布专用模块,您需要是付费专用模块用户

公共范围的模块是免费的,不需要付费订阅。要发布公共范围的模块,请在发布时设置访问选项。对于所有后续发布,此选项将保持设置状态。

npm publish --access=public

TIPS:这里给一个小小的建议,作用域包的目录结构最好按照包名的层级结构去创建,即@username作为父目录名称,project-name作为子目录名称

└─@username
    └─project-name1
    └─project-name2

使用作用域包和使用普通包没什么区别,直接用package.json里的name属性值即可。

{
  "dependencies": {
    "@username/project-name": "^1.0.0"
  }
}

– end –


评论