NPM 简介
背景
前端在工程化的路上涌现出了一大堆第三方库,像最基础的JQuery、Moment到更先进的框架Vue、React。这些库会相互依赖。如果没有包管理器的存在,我们想要使用一个库,就必须先把这个库依赖的所有其他的库下载下来,而这些其他的库也有可能依赖另外的库,然后我们又先得把那些库下载下来,如此往复...
这还是在版本没有冲突的情况下,如果依赖的版本冲突了,比如说我要用 a,而 a 依赖 z ver1.0。我又要用 b,而 b 依赖 z ver2.0,那这个时候我就要下载两个版本的 z 并让 a 和 b 分别依赖它们需要的版本...
是不是光听起来就头疼了。
没错,如果现在还让程序员去一个一个下载依赖,那肯定会疯掉的。但是我们是谁,我们是程序员,我们的宗旨是:
能让代码做到的绝不亲手做
于是,npm(node package manager)出现了。
包管理器的功能
包管理器,顾名思义,就是帮助我们管理各种包的相互依赖以及版本问题的管理器。
以后要是用到某个第三方库了,跟包管理器说,它会自动找到这个第三方库所依赖的库并下载,并且自动递归处理这些依赖的库所依赖的库。当然,都是合适的版本。
npm
npm (node package manager)就是最经典的,最基础的包管理器。
配置文件 package.json
随着开发,需要依赖的包会越来越多,node_module里面的文件会越来越多。所以我们在发布项目到服务器的时候,会不把这个文件夹携带过去。但是这样问题来了,服务器怎么知道并恢复需要用到的依赖呢(代码移植)?
这个时候,package.json就来了。这个文件的主要作用就是在开发的时候记录依赖及版本号。然后服务器就可以根据这个文件重新下载文件以恢复依赖及版本号。
当然,配置文件还会记录其他信息:
name包名version版本description描述homepage官网地址author作者repository仓库main入口文件keywords关键字
这些东西会在创建 npm init 的时候询问之后就会形成 package.json 文件
{
"name": "...",
"version": "...",
"description": "...",
"main": "...",
"scripts": {
"test": "..."
},
"repository": {
"type": "...",
"url": "..."
},
"author": "...",
"license": "...",
"bugs": {
"url": "..."
},
"homepage": "...",
"dependencies": {
"jquery": "..."
},
"devDependencies": {
"mocha": "..."
}
}
使用包
CommonJS
const $ = require("jquery");
ES Module
import $ from "jquery";
不管是用哪一种方式使用,查找文件方式都是一致的:
- 首先在当前文件夹内寻找
node_module/jquery.js。有,直接返回 - 寻找
node_module/jquery/main。有,直接返回main是指入口文件。在package.json文件里面有注明是哪个文件。默认是index.js
- 返回上一层文件夹,如果到了根目录还是没有,报错。否则继续第1步
版本控制
背景
包不是一尘不变的,会持续更新,也就形成了很多版本号。一般来说,版本号的结构是这么组成的:
大版本号.次版本号.修订号
大版本号有重大改变,一般不会兼容前面的版本了次版本号有改变,但一般不至于不兼容前面的版本,比如增加一些API什么的修订号有改变,但很小,一般都是修修bug之类的
那比如说,如果 a 要依赖 z ver1.0.0,然后后来 z 更新到 ver2.0.0,那么在恢复依赖的时候,不加版本控制的 a 可能会因为下载了 z 的更高版本而不能正常工作了。
解决方法很简单,就直接指定 z ver1.0.0 就行,其他一概不允许私自升级。
但是问题也很明显,就是不灵活。如果 z 只是更新了小版本 1.1.0,或者只是更新了补丁 1.0.1,那这样子限制就太狠了。在恢复依赖的时候就不得不多下载很多很多版本,即便它们都可以正常工作。
这是一个比较复杂的问题,于是孕育出来了一个版本控制方法。
语义版本
不知道大家有没有发现,有的时候 npm install 之后,package.json 里面会记录的包的版本很奇怪:
{
...
"dependencies": {
"jquery": "^3.4.1"
}
...
}
你说你 jquery 下载的版本是 3.4.1 就 3.4.1 呗,整个 ^ 是怎么回事呢?其实,这个就是npm如何管理版本的方法。
| 符号 | 语义 |
|---|---|
> | 版本号可以比我规定的高 |
>= | 版本号可以比我规定的高,或跟我一样 |
< | 版本可以比我规定的低 |
<= | 版本号可以比我规定的低,或跟我一样 |
~ | 修订号可以比我规定的高 |
^ | 次版本号和修订号可以比我规定的高 |
X | 占位符 |
package-lock.json vs package.json
package-lock.json 会记录安装包的确切版本。如果移植工程的时候有这个文件,就会按照这个文件所规定的版本号恢复依赖。
npm脚本
在 package.json 中,可以定义一些常用的命令,这样就不用每次都输入一些繁琐的命令了:
{
...
"scripts": {
"命令别名": "命令"
}
...
}
在使用的时候,可以直接 npm run 命令别名,相当于执行了 命令
另外一些常用的命令,可以省略 run,像是 start stop test
命令 可以省略 npx,因为它会自动把命令环境变成 node_module/bin/
npm [run] start 有默认值:node server.js
npm 常用命令【持续更新】
获取源
npm config get registry
设置源
NEW_REGISTRY:新设置的源的地址
npm config set registry NEW_REGISTRY
安装
本地安装(基础款)
PACKAGE_NAME:要下载的包的名字
npm install PACKAGE_NAME ...
或者
npm i PACKAGE_NAME ...
全局安装
在基础款的基础上加上标签--global或者-g
PACKAGE_NAME:要下载的包的名字
npm install --global PACKAGE_NAME ...
或者
npm i -g PACKAGE_NAME ...
精确安装
npm i --save-exact PACKAGE_NAME
npm i -E
安装指定版本号
npm i PACKAGE_NAME@VERSION
恢复依赖(开发环境)
npm install
或者
npm i
恢复依赖(生产环境)
npm install --production
生产环境依赖
下载依赖,并记录依赖为生产环境依赖。即记录在 "dependencies" 里
PACKAGE_NAME:要下载的包的名字
npm i [--save|-S]? PACKAGE_NAME
开发环境依赖
下载依赖,并记录依赖为开发环境依赖。即记录在 "devDependencies" 里
PACKAGE_NAME:要下载的包的名字
npm i [--save-dev|-D] PACKAGE_NAME
操作node_modules
在包名前面加上 npx,npm就会到当前目录的 ./node_modules/bin/ 中找到对应的包并调用
PACKAGE_NAME:要操作的包的名字
npx PACKAGE_NAME
全局配置地址
npm config get prefix
包配置
npm init
查看安装路径
npm root // local
npm root -g // global
查看包信息
npm [view|v|show|info|] PACKAGE_NAME
查询安装包
npm [list|ls|la|ll] [depth=DEPTH_NUMBER]
查看更新
npm outdated
更新
npm [update|up|upgrade]
卸载包
npm [uninstall|remove|rm|r|un|unlink] PACKAGE_NAME
npm配置
// list out configs
npm config ls [-l] [--json]
// get a config
npm config get CONFIG_NAME
// set a config
npm config set CONFIG_NAME=CONFIG_VALUE
// remove a config
npm config delete CONFIG_NAME