Node.js之npm工具介绍与使用

277 阅读17分钟

一、npm介绍

1、概念

npm(Node Package Manager)是一个基于Node.js的包管理器,也是整个Node.js社区最流行、支持的第三方模块最多的包管理器。常见的使用场景有以下几种:

  • 允许用户从NPM服务器下载别人编写的第三方包到本地使用。
  • 允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。
  • 允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。

1.1 Node.js安装

  • 安装Node.js时会自动安装npm包。

Node.js下载

历史版本下载:nodejs.org/dist/

下载完成后双击安装

第一步:点击next

image.png

第二步:勾选接受协议选项,点击 next(下一步)

image.png

第三步:Node.js默认安装目录为 "C:\Program Files\nodejs" , 可以修改目录,并点击 next(下一步)

image.png

第四步:点击树形图标来选择你需要的安装模式 , 然后点击下一步 next(下一步)

image.png

第五步:点击 Install(安装) 开始安装Node.js。你也可以点击 Back(返回)来修改先前的配置。 然后并点击 next(下一步)

image.png

第六步:点击 Finish(完成)按钮退出安装向导

image.png

  • 在node.js安装目录下创建两个目录文件node_global和node_cache

如果没有设置全局目录node_global,那么全局安装的文件将会保存到 C:\Users\hades\AppData\Roaming\npm (hases是自己设置的计算机名字)。所以,安装好node后,要设置一下node_global和node_cache(node缓存文件夹)

image.png

  • win+r打开cmd窗口设置
npm config set prefix "C:\Program Files\nodejs\node_global" 
npm config set cache "C:\Program Files\nodejs\node_cache"
  • 设置淘宝镜像
npm install -g cnpm --registry=https://registry.npm.taobao.org
  • 卸载

npm uninstall <name>删除模块node_modules,但不是package.json

npm uninstall <name> --save也删除它dependenciespackage.json

npm uninstall <name> --save-dev也删除它devDependenciespackage.json

npm -g uninstall <name> --save 也会全局删除它

  • 环境变量配置

在系统环境变量里,添加一个NODE_PATH变量,如下:
点击[我的电脑] -->属性–> 高级系统变量 -->环境变量 --> 系统变量 -->新建:
变量名:NODE_PATH
路径:C:\Program Files\nodejs\node_global\node_modules;

image.png

同时,将上面的C:\Program Files\nodejs\node_global\node_modules路径,也添加到[用户变量]的Path里

image.png

  • 查看node版本
node -v
  • 查看npm版本
npm -v

1.2 npm安装

如果是 Window 系统使用以下命令即可

npm install npm -g  // -g表示全局安装

二、package.json与package-lock.json

1、package.json文件

1.1 package.json文件作用

项目描述文件,记录了当前项目信息,例如项目名称、版本、作者、github地址、 当前项目依赖了哪些第三方模块等。 package.json文件在项目根目录下使用npm init -y命令生成

在传项目给用户时可以把node_modules文件删除,让用户在此项目根目录下输入命令:npm install,系统会查看dependencies(项目依赖的第三方模块)里面的内容,自动把包下载完,生成node_modules文件

1.2 package.json文件事例

{
  "name": "exchange",
  "version": "0.1.0",
  "author": "zhangsan <zhangsan@163.com>",
  "description": "第一个node.js程序",
  "keywords":["node.js","javascript"],
  "private": true,
  "bugs":{"url":"http://path/to/bug","email":"bug@example.com"},
  "contributors":[{"name":"李四","email":"lisi@example.com"}],
  "repository": {
		"type": "git",
		"url": "https://path/to/url"
	},
  "homepage": "http://necolas.github.io/normalize.css",
  "license":"MIT",
  "dependencies": {
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-router-dom": "^5.0.1",
    "react-scripts": "3.0.1"
  },
  "devDependencies": {
    "browserify": "~13.0.0",
    "karma-browserify": "~5.0.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "bin": {
  "webpack": "./bin/webpack.js"
  },
  "main": "lib/webpack.js",
  "module": "es/index.js",
  "eslintConfig": {
    "extends": "react-app"
  },
  "engines" : { 
    "node" : ">=0.10.3 <0.12" 
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "style": [
  "./node_modules/tipso/src/tipso.css"
],
  "files": [
    "lib/",
    "bin/",
    "buildin/",
    "declarations/",
    "hot/",
    "web_modules/",
    "schemas/",
    "SECURITY.md"
  ]
}

文件配置说明

  • name:项目/模块名称,长度必须小于等于214个字符,不能以"."(点)或者"_"(下划线)开头,不能包含大写字母。
  • version:项目版本。
  • author:项目开发者,它的值是你在npmjs.org网站的有效账户名,遵循“账户名<邮件>”的规则,例如:zhangsan zhangsan@163.com
  • description:项目描述,是一个字符串。它可以帮助人们在使用npm search时找到这个包。
  • keywords:项目关键字,是一个字符串数组。它可以帮助人们在使用npm search时找到这个包。
  • private:是否私有,设置为 true 时,npm 拒绝发布。
  • license:软件授权条款,让用户知道他们的使用权利和限制。
  • bugs:bug 提交地址。
  • contributors:项目贡献者。
  • repository:项目仓库地址。
  • homepage:项目包的官网 URL。
  • dependencies:生产环境下,项目运行所需依赖。
  • devDependencies:开发环境下,项目所需依赖。
  • scripts:执行 npm 脚本命令简写,比如 “start”: “react-scripts start”, 执行 npm\start 就是运行 “react-scripts start”。
  • bin:内部命令对应的可执行文件的路径。
  • main:项目默认执行文件,比如 require(‘webpack’);就会默认加载 lib 目录下的 webpack.js 文件,如果没有设置,则默认加载项目跟目录下的 index.js 文件。
  • module:是以 ES Module(也就是 ES6)模块化方式进行加载,因为早期没有 ES6 模块化方案时,都是遵循CommonJS 规范,而 CommonJS 规范的包是以 main 的方式表示入口文件的,为了区分就新增了 module 方式,但是 ES6 模块化方案效率更高,所以会优先查看是否有 module 字段,没有才使用 main 字段。
  • eslintConfig:EsLint 检查文件配置,自动读取验证。
  • engines:项目运行的平台。
  • browserslist:供浏览器使用的版本列表。
  • style:供浏览器使用时,样式文件所在的位置;样式文件打包工具parcelify,通过它知道样式文件的打包位置。
  • files:被项目包含的文件名数组。

2、package.json文件与package-lock.json文件的区别。

2.1 package.json文件

package.json 用来描述项目及项目所依赖的模块信息。 ,就是帮我们管理项目中的依赖包的,让我们远离了依赖地狱。通过 npm 管理,使用一些简单的命令,自动生成package.json, 安装包依赖关系都由package.json来管理,我们几乎不必考虑它们。

2.2 package-lock.json文件

官方文档是这样解释的:package-lock.json 它会在 npm 更改 node_modules 目录树 或者 package.json 时自动生成的 ,它准确的描述了当前项目npm包的依赖树,并且在随后的安装中会根据 package-lock.json 来安装,保证是相同的一个依赖树,不考虑这个过程中是否有某个依赖有小版本的更新。

它的产生就是来对整个依赖树进行版本固定的(锁死)。

当我们在一个项目中npm install时候,会自动生成一个package-lock.json文件,和package.json在同一级目录下。package-lock.json记录了项目的一些信息和所依赖的模块。这样在每次安装都会出现相同的结果. 不管你在什么机器上面或什么时候安装。

当我们下次再npm install时候,npm 发现如果项目中有 package-lock.json 文件,会根据 package-lock.json 里的内容来处理和安装依赖而不再根据 package.json

注意,使用cnpm install时候,并不会生成 package-lock.json 文件,也不会根据 package-lock.json 来安装依赖包,还是会使用 package.json 来安装。

3、package-lock.json生成逻辑

简单描述一下 package-lock.json 生成的逻辑。假设我们现在有三个 package,在项目 lock-test中,安装依赖A,A项目面有B,B项目面有C

// package lock-test
{ "name": "lock-test", "dependencies": { "A": "^1.0.0" }}
// package A
{ "name": "A", "version": "1.0.0", "dependencies": { "B": "^1.0.0" }}
// package B
{ "name": "B", "version": "1.0.0", "dependencies": { "C": "^1.0.0" }}
// package C
{ "name": "C", "version": "1.0.0" }

在这种情况下 package-lock.json, 会生成类似下面铺平的结构

// package-lock.json
{ 
    "name": "lock-test",  
    "version": "1.0.0",  
    "dependencies": {    
        "A": { "version": "1.0.0" },
        "B": { "version": "1.0.0" },
        "C": { "version": "1.0.0" }  
    }
}

如果后续无论是直接依赖的 A 发版,或者间接依赖的B, C 发版,只要我们不动 package.jsonpackage-lock.json 都不会重新生成。

A 发布了新版本 1.1.0,虽然我们 package.json 写的是 ^1.0.0 但是因为 package-lock.json 的存在,npm i 并不会自动升级,我们可以手动运行 npm i A@1.1.0 来实现升级。

因为 1.1.0 package-lock.json 里记录的 A@1.0.0 是不一致的,因此会更新 package-lock.json 里的 A 的版本为 1.1.0。

B 发布了新版本 1.0.1, 1.0.2, 1.1.0, 此刻如果我们不做操作是不会自动升级 B 的版本的,但如果此刻 A 发布了 1.1.1,虽然并没有升级 B 的依赖,但是如果我们项目里升级 A@1.1.1,此时 package-lock.json 里会把 B 直接升到 1.1.0 ,因为此刻^1.0.0的最新版本就是 1.1.0。

经过这些操作后 项目 lock-test 的 package.json 变成

// package 
lock-test{ "dependencies": { "A": "^1.1.0" }}

对应的 package-lock.json 文件

{  
    "name": "lock-test",  
    "version": "1.0.0",
    "dependencies": {  
        "A": { "version": "1.1.0" },
        "B": { "version": "1.1.0" },
        "C": { "version": "1.0.0" }
    }
}


这个时候我们将 B 加入我们 lock-test 项目的依赖, B@^1.0.0,package.json如下

{ "dependencies": { "A": "^1.1.0", "B": "^1.0.0" }}

我们执行这个操作后,package-lock.json 并没有被改变,因为现在 package-lock.jsonB@1.1.0 满足 ^1.0.0 的要求

但是如果我们将 B 的版本固定到 2.x 版本, package-lock.json 就会发生改变

{ "dependencies": { "A": "^1.1.0", "B": "^2.0.0" }}

因为存在了两个冲突的B版本,package-lock.json 文件会变成如下形式

{  
    "name": "lock-test",
    "version": "1.0.0",  
    "dependencies": {    
        "A": {      
            "version": "1.1.0",      
            "dependencies": {        
                "B": { "version": "1.1.0" }      
            }    
        },    
        "B": { "version": "2.0.0" },    
        "C": { "version": "1.0.0" }  
    }
}

因为 B 的版本出现了冲突,npm 使用嵌套描述了这种行为

我们实际开发中并不需要关注这种生成的算法逻辑,我们只需要了解,package-lock.json 的生成逻辑是为了能够精准的反映出我们 node_modules 的结构,并保证能够这种结构被还原。

4、package-lock.json可能被修改的原因

4.1 package.json 文件修改了

4.2 挪动了包的位置

将部分包的位置从 dependencies 移动到 devDependencies 这种操作,虽然包未变,但是也会影响 package-lock.json,会将部分包的 dev 字段设置为 true

4.3 registry 的影响

经过实际使用发现,如果我们 node_modules 文件夹下的包中下载时,就算版本一样,安装源 registry 不同,执行 npm i 时也会修改 package-lock.json

可能还存在其他的原因,但是 package-lock.json 是不会无缘无故被更改的,一定是因为 package.json 或者 node_modules 被更改了,因为 正如上面提到的 package-lock.json 为了能够精准的反映出我们 node_modules 的结构。

4.4 开发建议

一般情况下 npm install 是可以的,他能保证根据 package-lock.json 还原出开发时的node_modules

但是为了防止出现刚刚提到的意外情况,除非涉及到对包的调整,其他情况下建议使用 npm ci 来安装依赖,会避免异常的修改 package-lock.json

持续集成工具中更推荐是用 npm ci,保证构建环境的准确性npm i 和 npm ci 的区别 可以参考官方文档 npm-ci

三、Semantic versioning(语义版本控制)

1、SemVer介绍

在软件管理的领域里存在着被称作 “依赖地狱” 的死亡之谷,系统规模越大,加入的包越多,你就越有可能在未来的某一天发现自己已深陷绝望之中。

在依赖高的系统中发布新版本包可能很快会成为噩梦。如果依赖关系过高,可能面临版本控制被锁死的风险(必须对每一个依赖包改版才能完成某次升级)。而如果依赖关系过于松散,又将无法避免版本的混乱(假设兼容于未来的多个版本已超出了合理数量)。当你项目的进展因为版本依赖被锁死或版本混乱变得不够简便和可靠,就意味着你正处于依赖地狱之中。

作为这个问题的解决方案之一,我提议用一组简单的规则及条件来约束版本号的配置和增长。这些规则是根据(但不局限于)已经被各种封闭、开放源码软件所广泛使用的惯例所设计。为了让这套理论运作,你必须先有定义好的公共 API。这可以透过文件定义或代码强制要求来实现。无论如何,这套 API 的清楚明了是十分重要的。一旦你定义了公共 API,你就可以透过修改相应的版本号来向大家说明你的修改。考虑使用这样的版本号格式:X.Y.Z(主版本号.次版本号.修订号) 修复问题但不影响 API 时,递增修订号;API 保持向下兼容的新增及修改时,递增次版本号;进行不向下兼容的修改时,递增主版本号。

我称这套系统为 “语义化的版本控制”,在这套约定下,版本号及其更新方式包含了相邻版本间的底层代码和修改内容的信息。

2、版本标准

SemVer规范的标准版本号采用 X.Y.Z 的格式,其中 X、Y 和 Z 为非负的整数,且禁止在数字前方补零。X 是主版本号、Y 是 次版本号 、而 Z 为 修订号 。每个元素必须以数值来递增。

  • 主版本号(major):当你做了不兼容的API 修改
  • 次版本号(minor):当你做了向下兼容的功能性新增
  • 修订号(patch):当你做了向下兼容的问题修正。

major:新的架构调整,不兼容老版本

patch:修复bug,兼容老版本

minor:新增功能,兼容老版本

  • version

必须匹配某个版本

:1.1.2,表示必须依赖1.1.2版

  • >version

必须大于某个版本

:>1.1.2,表示必须大于1.1.2版

  • >=version

可大于或等于某个版本

:>=1.1.2,表示可以等于1.1.2,也可以大于1.1.2版本

  • <version

必须小于某个版本 

:<1.1.2,表示必须小于1.1.2版本

  • <=version

可以小于或等于某个版本

:<=1.1.2,表示可以等于1.1.2,也可以小于1.1.2版本

  • ~version

大概匹配某个版本

如果minor版本号指定了,那么minor版本号不变,而patch版本号任意

如果minor和patch版本号未指定,那么minor和patch版本号任意

如:~1.1.2,表示>=1.1.2 <1.2.0,可以是1.1.2,1.1.3,1.1.4,.....,1.1.n 

如:~1.1,表示>=1.1.0 <1.2.0,可以是同上

如:~1,表示>=1.0.0 <2.0.0,可以是1.0.0,1.0.1,1.0.2,.....,1.0.n,1.1.n,1.2.n,.....,1.n.n

  • ^version

兼容某个版本

版本号中最左边的非0数字的右侧可以任意

如果缺少某个版本号,则这个版本号的位置可以任意

如:^1.1.2 ,表示>=1.1.2 <2.0.0,可以是1.1.2,1.1.3,.....,1.1.n,1.2.n,.....,1.n.n

如:^0.2.3 ,表示>=0.2.3 <0.3.0,可以是0.2.3,0.2.4,.....,0.2.n

如:^0.0,表示 >=0.0.0 <0.1.0,可以是0.0.0,0.0.1,.....,0.0.n

  • x-range

x的位置表示任意版本

如:1.2.x,表示可以1.2.0,1.2.1,.....,1.2.n

  • -range

任意版本, ""也表示任意版本

如:*,表示>=0.0.0的任意版本

  • version1 - version2

大于等于version1,小于等于version2

如:1.1.2 - 1.3.1,表示包括1.1.2和1.3.1以及他们件的任意版本

  • range1 || range2

满足range1或者满足range2,可以多个范围

如:<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0,表示满足这3个范围的版本都可以

四、npm 常用命令

1、npm install 安装模块

npm install (with no args, in package dir)
npm install [<@scope>/]<name>
npm install [<@scope>/]<name>@<tag>
npm install [<@scope>/]<name>@<version>
npm install [<@scope>/]<name>@<version range>
npm install <tarball file>
npm install <tarball url>
npm install <folder>

alias: npm i
common options: [-S|--save|-D|--save-dev|-O|--save-optional] [-E|--save-exact] [--dry-run]
  • 安装包,默认会安装最新的版本
npm install express
  • 安装指定版本
npm install express@3.9.1

安装包并将信息保持到项目的package.json文件中

  • -S, --save 安装包信息将加入到dependencies(生产阶段的依赖)
npm install gulp --save 或 npm install gulp -S
  • -D, --save-dev 安装包信息将加入到devDependencies(开发阶段的依赖),所以开发阶段一般使用它
npm install gulp --save-dev 或 npm install gulp -D
  • -O, --save-optional 安装包信息将加入到optionalDependencies(可选阶段的依赖)
npm install gulp --save-optional 或 npm install gulp -O
  • -E, --save-exact 精确安装指定模块版本
npm install gulp --save-exact 或 npm install gulp -E

输入命令npm install gulp -ES, 留意package.json 文件的 dependencies 字段,以看出版本号中的 ^ 消失了

模块的依赖都被写入了package.json文件后,他人打开项目的根目录(项目开源、内部团队合作),使用npm install命令可以根据dependencies配置安装所有的依赖包

npm install
  • 本地安装(local)
npm install gulp
  • 全局安装(global),使用 -g 或 --global
npm install gulp -g

2、npm uninstall 卸载模块

  • 基础语法
npm uninstall [<@scope>/]<pkg>[@<version>]... [-S|--save|-D|--save-dev|-O|--save-optional]

aliases: remove, rm, r, un, unlink
  • 如卸载开发版本的模块
npm uninstall gulp --save-dev

3、npm update 更新模块

  • 基础语法
npm update [-g] [<pkg>...]

4、npm outdated 检查模块是否已经过时

  • 基础语法
npm outdated [[<@scope>/]<pkg> ...]

此命令会列出所有已经过时的包,可以及时进行包的更新

image.png

5、npm ls 查看安装的模块

  • 基础语法
npm ls [[<@scope>/]<pkg> ...]

aliases: list, la, ll
  • 查看全局安装的模块及依赖
npm ls -g 

6、npm init 在项目中引导创建一个package.json文件

安装包的信息可保持到项目的package.json文件中,以便后续的其它的项目开发或者他人合作使用,也说package.json在项目中是必不可少的。

npm init [-f|--force|-y|--yes]

7、npm help 查看某条命令的详细帮助

  • 基础语法
npm help <term> [<terms..>]

例如输入

npm help install

系统在默认的浏览器或者默认的编辑器中打开本地nodejs安装包的文件

8、npm root 查看包的安装路径

输出 node_modules的路径

npm root [-g]

D:\upyun\test\node_modules

9、npm config 管理npm的配置路径

npm config set <key> <value> [-g|--global]
npm config get <key>
npm config delete <key>
npm config list
npm config edit
npm get <key>
npm set <key> <value> [-g|--global]

对于config这块用得最多应该是 设置代理,解决npm安装一些模块失败的问题

又如国内的网络环境问题,某官方的IP可能被和谐了,幸好国内有好心人,搭建了镜像,此时我们简单设置镜像

npm config set registry="http://r.cnpmjs.org"

也可以临时配置,如安装淘宝镜像

npm install -g cnpm --registry=https://registry.npm.taobao.org

10、npm cache 管理模块的缓存

npm cache add <tarball file>
npm cache add <folder>
npm cache add <tarball url>
npm cache add <name>@<version>

npm cache ls [<path>]

npm cache clean [<path>]

最常用命令无非清除npm本地缓存

npm cache clean

11、npm start 启动模块

  • 基础语法
npm start [-- <args>]

该命令写在package.json文件scripts的start字段中,可以自定义命令来配置一个服务器环境和安装一系列的必要程序,如

"scripts": {
    "start": "gulp -ws"
}

此时在cmd中输入npm start命令相当于执行gulpfile.js文件自定义的watch和server命令。

如果package.json文件没有设置start,则将直接启动node server.js

12、npm stop 停止模块

  • 基础语法
npm stop [-- <args>]

13、npm restart 重新启动模块

  • 基础语法
npm restart [-- <args>]

14、npm test 测试模块

  • 基础语法
npm test [-- <args>]
npm tst [-- <args>]

该命令写在package.json文件scripts的test字段中,可以自定义该命令来执行一些操作,如

"scripts": {
    "test": "gulp release"
},

此时在cmd中输入npm test命令相当于执行gulpfile.js文件自定义的release命令。

15、npm version 查看模块版本

  • 基础语法
npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease | from-git]

'npm [-v | --version]' to print npm version
'npm view <pkg> version' to view a package's published version
'npm ls' to inspect current package/dependency versions

查看模块的版本

npm version

image.png

16、npm view 查看模块的注册信息

  • 基础语法
npm view [<@scope>/]<name>[@<version>] [<field>[.<subfield>]...]

aliases: info, show, v
  • 查看模块的依赖关系
npm view gulp dependencies

image.png

  • 查看模块的源文件地址
npm view gulp repository.url
  • 查看模块的贡献者,包含邮箱地址
npm view npm contributors

17、npm adduser 用户登录

  • 基础语法
npm adduser [--registry=url] [--scope=@orgname] [--always-auth]

发布模板到npm社区前需要先登录,然后再进入发布的操作

18、 npm publish 发布模块

  • 基础语法
npm publish [<tarball>|<folder>] [--tag <tag>] [--access <public|restricted>]

Publishes '.' if no argument supplied
Sets tag 'latest' if no --tag specified

19、npm access 在发布的包上设置访问级别

  • 基础语法
npm access public [<package>]
npm access restricted [<package>]

npm access grant <read-only|read-write> <scope:team> [<package>]
npm access revoke <scope:team> [<package>]

npm access ls-packages [<user>|<scope>|<scope:team>]
npm access ls-collaborators [<package> [<user>]]
npm access edit [<package>]

五、package.json文件依赖项升级

1、npm安装依赖至指定版本

1.1 在 package.josn 文件中指定修改版本

先在package.json里修改好指定版本号(例:axios": "^0.19.2),然后输入:

npm update axios

1.2 npm 命令指定版本

npm update axios@0.19.2

会把webpack更新至指定版本,但是不会写到package.json文件里,如果需要写到package.json里执行如下命令:
npm update axios@4.7.0 --save

注:其中--save等于-S.

1.3 更新到最新版本

npm update axios@latest -S

–save和–save-dev区别
一句话:–save-dev是你开发时依赖的东西,–save是发布后还依赖的东西.

2、整体升级

首先安装升级插件 npm-check-updates

$ npm install -g npm-check-updates
# 或者
$ cnpm install -g npm-check-updates

ncu 是 npm-check-updates 的缩写命令 

输入ncu命令,可以看到需要升级安装包

# 查看更新
 ncu 

image.png

可以看到有好几个包要更新

# 查看所有ncu命令
ncu -h

例如使用ncu -a进行更新

# 更新
ncu -a 

使用ncu -u更新package.json

# 更新
ncu -u

image.png

更新完package.json以后,删除node_modules安装包,然后再重新下载

npm i

或者

cnpm i