1.包的安装
安装(install)即下载包 由于 npm 的官方 registry 服务器位于国外,可能受网速影响导致下载缓慢或失败。因此,安装好 npm 之后,需要重新设置 registry 的地址为国内地址。目前,淘宝 https://registry.npm.taobao.org 提供了国内的 registry 地址,先设置到该地址。设置方式为npm config set registry https://registry.npm.taobao.org。设置好后,通过命令npm config get registry进行检查npm 安装一个包,分为两种安装方式:
本地安装
全局安装
本地安装
使用命令npm install 包名或npm i 包名即可完成本地安装
本地安装的包出现在当前目录下的node_modules目录中
随着开发的进展,node_modules目录会变得异常庞大,目录下的内容不适合直接传输到生产环境,因此通常使用.gitignore文件忽略该目录中的内容 本地安装适用于绝大部分的包,它会在当前目录及其子目录中发挥作用 通常在项目的根目录中使用本地安装 安装一个包的时候,npm 会自动管理依赖,它会下载该包的依赖包到node_modules目录中 如果本地安装的包带有 CLI,npm 会将它的 CLI 脚本文件放置到node_modules/.bin下,使用命令npx 命令名即可调用
全局安装
全局安装的包放置在一个特殊的全局目录,该目录可以通过命令npm config get prefix查看
使用命令npm install --global 包名 或 npm i -g 包名
重要:全局安装的包并非所有工程可用,它仅提供全局的 CLI 工具
大部分情况下,都不需要全局安装包,除非:
包的版本非常稳定,很少有大的更新
提供的 CLI 工具在各个工程中使用的非常频繁
CLI 工具仅为开发环境提供支持,而非部署环境
2.包配置
目前遇到的问题:
拷贝工程后如何还原?
如何区分开发依赖和生产依赖?
如果自身的项目也是一个包,如何描述包的信息
以上这些问题都需要通过包的配置文件解决
配置文件
npm 将每个使用 npm 的工程本身都看作是一个包,包的信息需要通过一个名称固定的配置文件来描述
配置文件的名称固定为:package.json
可以手动创建该文件,而更多的时候,是通过命令npm init创建的
配置文件中可以描述大量的信息,包括:
name:包的名称,该名称必须是英文单词字符,支持连接符
version:版本
版本规范:主版本号.次版本号.补丁版本号
主版本号:仅当程序发生了重大变化时才会增长,如新增了重要功能、新增了大量的API、技术架构发生了重大变化
次版本号:仅当程序发生了一些小变化时才会增长,如新增了一些小功能、新增了一些辅助型的API
补丁版本号:仅当解决了一些 bug 或 进行了一些局部优化时更新,如修复了某个函数的 bug、提升了某个函数的运行效率
或者直接npm init --yes
description:包的描述
homepage:官网地址
author:包的作者,必须是有效的 npm 账户名,书写规范是 account ,例如:zhangsan [email protected],不正确的账号和邮箱可能导致发布包时失败
repository:包的仓储地址,通常指 git 或 svn 的地址,它是一个对象
type:仓储类型,git 或 svn
url:地址
main:包的入口文件,使用包的人默认从该入口文件导入包的内容
keywords: 搜索关键字,发布包后,可以通过该数组中的关键字搜索到包
使用npm init --yes或npm init -y可以在生成配置文件时自动填充默认配置
保存依赖关系
大部分时候,我们仅仅是开发项目,并不会把它打包发布出去,尽管如此,我们仍然需要package.json文件
package.json文件最重要的作用,是记录当前工程的依赖
dependencies:生产环境的依赖包
devDependencies:仅开发环境的依赖包
配置好依赖后,使用下面的命令即可安装依赖
本地安装所有依赖 dependencies + devDependencies
npm install
npm i
仅安装生产环境的依赖 dependencies
npm install --production
这样一来,代码移植就不是问题了,只需要移植源代码和package.json文件,不用移植node_modules目录,然后在移植之后通过命令即可重新恢复安装
为了更加方便的添加依赖,npm支持在使用install命令时,加入一些额外的参数,用于将安装的依赖包保存到package.json文件中
涉及的命令如下
安装依赖到生产环境
npm i 包名
npm i --save 包名
npm i -S 包名
安装依赖到开发环境
npm i --save-dev 包名
npm i -D 包名
自动保存的依赖版本,例如^15.1.3,这种书写方式叫做语义版本号(semver version)。
3.包的使用
nodejs 对 npm 支持非常良好
当使用 nodejs 导入模块时,如果模块路径不是以 ./ 或 …/ 开头,则 node 会认为导入的模块来自于 node_modules 目录,例如:
var _ = require("lodash");
它首先会从当前目录的以下位置寻找文件
node_modules/lodash.js
node_modules/lodash/入口文件
若当前目录没有这样的文件,则会回溯到上级目录按照同样的方式查找
如果到顶级目录都无法找到文件,则抛出错误
上面提到的入口文件按照以下规则确定
查看导入包的package.json文件,读取main字段作为入口文件
若不包含main字段,则使用index.js作为入口文件
入口文件的规则同样适用于自己工程中的模块 在 node 中,还可以手动指定路径来导入相应的文件,这种情况比较少见
2-5语义版本
思考:如果你编写了一个包A,依赖另外一个包B,你在编写代码时,包B的版本是2.4.1,你是希望使用你包的人一定要安装包B,并且是2.4.1版本,还是希望他可以安装更高的版本,如果你希望它安装更高的版本,高的什么程度呢?回顾:版本号规则
版本规范:主版本号.次版本号.补丁版本号
主版本号:仅当程序发生了重大变化时才会增长,如新增了重要功能、新增了大量的API、技术架构发生了重大变化
次版本号:仅当程序发生了一些小变化时才会增长,如新增了一些小功能、新增了一些辅助型的API
补丁版本号:仅当解决了一些 bug 或 进行了一些局部优化时更新,如修复了某个函数的 bug、提升了某个函数的运行效率
有的时候,我们希望:安装我的依赖包的时候,次版本号和补丁版本号是可以有提升的,但是主版本号不能变化
有的时候,我们又希望:安装我的依赖包的时候,只有补丁版本号可以提升,其他都不能提升
甚至我们希望依赖包保持固定的版本,尽管这比较少见
这样一来,就需要在配置文件中描述清楚具体的依赖规则,而不是直接写上版本号那么简单。
这种规则的描述,即语义版本
语义版本的书写规则非常丰富,下面列出了一些常见的书写方式
符号 描述 示例 示例描述
大于某个版本 >1.2.1 大于1.2.1版本
= 大于等于某个版本 >=1.2.1 大于等于1.2.1版本
< 小于某个版本 <1.2.1 小于1.2.1版本
<= 小于等于某个版本 <=1.2.1 小于等于1.2.1版本
- 介于两个版本之间 1.2.1 - 1.4.5 介于1.2.1和1.4.5之间
x 不固定的版本号 1.3.x 只要保证主版本号是1,次版本号是3即可
~ 补丁版本号可增 ~1.3.4 保证主版本号是1,次版本号是3,补丁版本号大于等于4
^ 此版本和补丁版本可增 ^1.3.4 保证主版本号是1,次版本号可以大于等于3,补丁版本号可以大于等于4
- 最新版本 * 始终安装最新版本
避免还原的差异
版本依赖控制始终是一个两难的问题
如果允许版本增加,可以让依赖包的bug得以修复(补丁版本号),可以带来一些意外的惊喜(次版本号),但同样可能带来不确定的风险(新的bug)
如果不允许版本增加,可以获得最好的稳定性,但失去了依赖包自我优化的能力
而有的时候情况更加复杂,如果依赖包升级后,依赖也发生了变化,会有更多不确定的情况出现
基于此,npm 在安装包的时候,会自动生成一个 package-lock.json 文件,该文件记录了安装包时的确切依赖关系
当移植工程时,如果移植了 package-lock.json 文件,恢复安装时,会按照 package-lock.json 文件中的确切依赖进行安装,最大限度的避免了差异
[扩展]npm的差异版本处理
如果两个包依赖同一个包的不同版本,如下图
面对这种情况,在 node_modules 目录中,不会使用扁平的目录结构,而会形成嵌套的目录,如下图:
├── node_modules
│ ├── a
│ │ ├── node_modules
│ │ │ ├── c
│ │ │ | |—— c包的文件
│ │ │── a包的文件
│ ├── b
│ │ ├── node_modules
│ │ │ ├── c
│ │ │ | |—— c包的文件
│ │ │── b包的文件
2-6npm 脚本 (npm scripts)
在开发的过程中,我们可能会反复使用很多的 CLI 命令,例如: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200309201454522.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyMTc3NDc4,size_16,color_FFFFFF,t_70)启动工程命令(node 或 一些第三方包提供的CLI命令)
部署工程命令(一些第三方包提供的CLI命令)
测试工程命令(一些第三方包提供的CLI命令)
这些命令纷繁复杂,根据第三方包的不同命令也会不一样,非常难以记忆
于是,npm 非常贴心的支持了脚本,只需要在 package.json 中配置 scripts 字段,即可配置各种脚本名称
之后,我们就可以运行简单的指令来完成各种操作了
运行方式是 npm run 脚本名称
不仅如此,npm 还对某些常用的脚本名称进行了简化,下面的脚本名称是不需要使用run的:
start
stop
test
一些细节:
脚本中可以省略npx
start脚本有默认值:node server.js
2-7运行环境配置
我们书写的代码一般有三种运行环境:开发环境
生产环境
测试环境
有的时候,我们可能需要在 node 代码中根据不同的环境做出不同的处理
如何优雅的让 node 知道处于什么环境,是极其重要的
通常我们使用如下的处理方式:
node中有一个全局变量 global (可以类比浏览器环境的window),该变量是一个对象,对象中的所有属性均可以直接使用
global有一个属性是process,该属性是一个对象,包含了当前运行node程序的计算机的很多信息,其中有一个信息是env,是一个对象,包含了计算机中所有的系统变量
通常,我们通过系统变量 NODE_ENV 的值,来判定node程序处于何种环境
有两种方式设置 NODE_ENV 的值
永久设置
临时设置
我们一般使用临时设置
因此,我们可以配置 scripts 脚本,在设置好了 NODE_ENV 后启动程序
为了避免不同系统的设置方式的差异,可以使用第三方库 cross-env 对环境变量进行设置
在node中读取package.json
有的时候,我们可能在 package.json 中配置一些自定义的字段,这些字段需要在node中读取
在node 中,可以直接导入一个json格式的文件,它会自动将其转换为js对象
2-8其他npm命令 {ignore}
**安装**精确安装最新版本
npm install --save-exact 包名
npm install -E 包名
安装指定版本
npm install 包名@版本号
查询
查询包安装路径
npm root [-g]
查看包信息
npm view 包名 [子信息]
view aliases:v info show
查询安装包
npm list [-g] [--depth=依赖深度]
list aliases: ls la ll
更新
检查有哪些包需要更新
npm outdated
更新包
npm update [-g] [包名]
update 别名(aliases):up、upgrade
卸载包
npm uninstall [-g] 包名
uninstall aliases: remove, rm, r, un, unlink
npm 配置
npm的配置会对其他命令产生或多或少的影响
安装好npm之后,最终会产生两个配置文件,一个是用户配置,一个是系统配置,当两个文件的配置项有冲突的时候,用户配置会覆盖系统配置
通常,我们不关心具体的配置文件,而只关心最终生效的配置
通过下面的命令可以查询目前生效的各种配置
npm config ls [-l] [--json]
另外,可以通过下面的命令操作配置
获取某个配置项
npm config get 配置项
设置某个配置项
npm config set 配置项=值
移除某个配置项
npm config delete 配置项
2-9发布包
准备工作
移除淘宝镜像源
到npm官网注册一个账号,并完成邮箱认证
本地使用 npm cli 进行登录
使用命令npm login登录
使用命令npm whoami查看当前登录的账号
使用命令npm logout注销
创建工程根目录
使
用npm init进行初始化
发布
开发
确定版本
使用命令npm publish完成发布
3-2yarn 的核心命令
初始化初始化:yarn init [--yes/-y]
安装
添加指定包:yarn [global] add package-name [--dev/-D] [--exact/-E]
安装package.json中的所有依赖:yarn install [--production/--prod]
脚本和本地CLI
运行脚本:yarn run 脚本名
start、stop、test可以省略run
运行本地安装的CLI:yarn run CLI名
查询
查看bin目录:
yarn [global] bin
查询包信息:yarn info 包名 [子字段]
列举已安装的依赖:yarn [global] list [--depth=依赖深度]
yarn的list命令和npm的list不同,yarn输出的信息更加丰富,包括顶级目录结构、每个包的依赖版本号
更新
列举需要更新的包:yarn outdated
更新包:yarn [global] upgrade [包名]
卸载
卸载包:yarn remove 包名
3-3yarn 的特别礼物
在终端命令上,yarn不仅仅是对npm的命令做了一个改名,还增加了一些原本没有的命令,这些命令在某些时候使用起来非常方便yarn check
使用yarn check命令,可以验证package.json文件的依赖记录和lock文件是否一致
这对于防止篡改非常有用
yarn audit
使用yarn audit命令,可以检查本地安装的包有哪些已知漏洞,以表格的形式列出,漏洞级别分为以下几种:
INFO:信息级别
LOW: 低级别
MODERATE:中级别
HIGH:高级别
CRITICAL:关键级别
yarn why
使用yarn why 包名命令,可以在控制台打印出为什么安装了这个包,哪些包会用到它
yarn create
非常有趣的命令
今后,我们会学习一些脚手架,所谓脚手架,就是使用一个命令来搭建一个工程结构
过去,我们都是使用如下的做法:
全局安装脚手架工具
使用全局命令搭建脚手架
由于大部分脚手架工具都是以create-xxx的方式命名的,比如react的官方脚手架名称为create-react-app
因此,可以使用yarn create命令来一步完成安装和搭建
例如:
yarn create react-app my-app
等同于下面的两条命令
yarn global add create-react-app
create-react-app my-app
4-1cnpm
官网地址:https://npm.taobao.org/为解决国内用户连接npm registry缓慢的问题,淘宝搭建了自己的registry,即淘宝npm镜像源
过去,npm没有提供修改registry的功能,因此,淘宝提供了一个CLI工具即cnpm,它支持除了npm publish以外的所有命令,只不过连接的是淘宝镜像源
如今,npm已经支持修改registry了,可能cnpm唯一的作用就是和npm共存,即如果要使用官方源,则使用npm,如果使用淘宝源,则使用cnpm
4-2nvm
nvm并非包管理器,它是用于管理多个node版本的工具在实际的开发中,可能会出现多个项目分别使用的是不同的node版本,在这种场景下,管理不同的node版本就显得尤为重要
nvm就是用于切换版本的一个工具
下载和安装
最新版下载地址:https://github.com/coreybutler/nvm-windows/releases
下载nvm-setup.zip后,直接安装
使用nvm
nvm提供了CLI工具,用于管理node版本
在终端中输入nvm,以查看各种可用命令
为了加快下载速度,建议设置淘宝镜像 node淘宝镜像:https://npm.taobao.org/mirrors/node/ npm淘宝镜像:https://npm.taobao.org/mirrors/npm/
pnpm
pnpm是一种新起的包管理器,从npm的下载量看,目前还没有超过yarn,但它的实现方式值得主流包管理器学习,某些开发者极力推荐使用pnpm
从结果上来看,它具有以下优势:
目前,安装效率高于npm和yarn的最新版
极其简洁的node_modules目录
避免了开发时使用间接依赖的问题
能极大的降低磁盘空间的占用
安装和使用
全局安装pnpm
4-3npm install -g pnpm
之后在使用时,只需要把npm替换为pnpm即可如果要执行安装在本地的CLI,可以使用pnpx,它和 npx 的功能完全一样,唯一不同的是,在使用pnpx执行一个需要安装的命令时,会使用pnpm进行安装
比如npx mocha执行本地的mocha命令时,如果mocha没有安装,则npx会自动的、临时的安装mocha,安装好后,自动运行mocha命令
pnpm原理
同 yarn 和 npm 一样,pnpm 仍然使用缓存来保存已经安装过的包,以及使用 pnpm-lock.yaml 来记录详细的依赖版本
不同于 yarn 和 npm, pnpm 使用符号链接和硬链接(可将它们想象成快捷方式)的做法来放置依赖,从而规避了从缓存中拷贝文件的时间,使得安装和卸载的速度更快
由于使用了符号链接和硬链接,pnpm可以规避windows操作系统路径过长的问题,因此,它选择使用树形的依赖结果,有着几乎完美的依赖管理。也因为如此,项目中只能使用直接依赖,而不能使用间接依赖
注意事项
由于 pnpm 会改动 node_modules 目录结构,使得每个包只能使用直接依赖,而不能使用间接依赖,因此,如果使用 pnpm 安装的包中包含间接依赖,则会出现问题(现在不会了,除非使用了绝对路径)
由于 pnpm 超高的安装卸载效率,越来越多的包开始修正之前的间接依赖代码