前端包管理器

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 超高的安装卸载效率,越来越多的包开始修正之前的间接依赖代码

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章