理解 npm

927 阅读7分钟

npm install 原理

你还只会npm install?

npm 是什么

npm即node package manager, 是node的包管理器.同样的包管理器还有yarn.

package

什么是package

package是一个包含程序和描述该程序的package.json的文件夹.

在执行npm install时,后边跟的包可以是以下形式:

  • 指向一个包的url,也可以指向本地的文件路径
  • 包的gzip压缩文件
  • 指向包的git仓库的url
  • <name>@<version>或者<name>@<tag>的字符串,指向的是远程register的包.

node_modules中package的结构

在早些时候包安装的方式是层级递归安装,就是在一级目录安装直接依赖,而每个包的依赖继续安装在自己包中的node_modules中,一直递归下去.

然而,这种安装方式会造成重复安装包和文件嵌套过深的问题.目前采用的方式是,将所有没有冲突的依赖包,都直接安装在顶层的node_modules中,如果那些有版本冲突的包则放在各个包自己目录的node_modules中.

从上图中可以看出,包的安装结构不再能反映依赖关系.所以npm又引入package.lock.json.其中的内容则完全反映了依赖的层级结构以及此时安装过的包的详细信息, 也就是相当于node_modules的快照.

为什么package.lock.json?

package.json中没有记录详细的包的信息,例如:安装来源和子依赖包的信息.所以每次按照package.json去安装极可能得到不同的安装结果. 并且package.json不能反映物理的包的安装结构, 如果每次包的安装顺序不同,可能最终安装的目录结构也是不同的.所以需要一个详细的lock文件来记录当前安装的信息,保证在不同的环境中,只要按照lock文件的方式,肯定会得到相同的安装结果.在npm 5 之前这种lock机制是通过shrinkwrap来实现的.

可以使用以下命令禁用lock:

npm config set package-lock false

npm script

npm script使用指南

原理

每次npm脚本运行就会新建一个shell, node_modules/.bin目录会被加入到环境中,shell退出时销毁.所以,npm脚本本质上是shell脚本.

包的package.jsonbin字段下,会指明命令和对于脚本文件,在执行install时将脚本拷贝都.bin目录下,全局安装包时,会在全局创建软连接到.bin目录下的文件.

使用方法

  • package.jsonscripts字段中添加要执行的脚本命令.
  • 向npm脚本传递参数需要使用--来分隔.
  • 像shell命令一样,可以使用&/&&来一次执行多条命令.
    • &表示并行.
    • &&表示继发执行,前一个成功才会执行后一个
    • 可以使用npm-run-all script-runner等工具来指定顺序.
  • npm脚本提供了hook,对于intall/start/test/uninstall/publish等都提供了pre/post的hook.
  • 简写形式.npm run start = npm start同样的还有test/stop
    • 注意npm restartnpm run stop && npm run restart && npm run start的简写
  • 变量.
    • 在脚本中使用npm_package_前缀可以访问package.json中的字段值
    • 使用npm_config_前缀可以访问npm配置中字段.

npx

  • 使用npx可以执行bin目录下的脚本.
  • 可以不用本地安装, 执行npm远程源中的包.
  • 可以用来使用指定版本的node.
  • 可以一键执行 GitHub Gist

npm中的配置

配置方法

  • 在命令行中指定.
    • 通过--来标识参数,例如--foo,表示要设置foo这个参数,--foo bar则表示将foo=bar.
    • --foo表示foo=true
  • 使用环境变量.
    • 形如npm_config_的环境变量会被认为是npm的配置参数.
  • 使用.npmrc文件.
    • .npmrc文件是npm配置信息存储的地方,可以手动修改这个文件来达到设置配置信息的目的.
    • 配置文件的读取顺序:
      1. 项目工程
      2. 用户目录.一般在~/.npmrc.使用npm config get userconfig可以获取位置.
      3. 全局目录.使用npm config get globalconfig可以查看全局配置的保存位置.
      4. npm的安装目录.
  • 以上配置的优先级,从上到下依次递减.

Per-Package Config Settings

Per-Package Config Settings意思是通过npm的配置来覆盖package.json中的config字段中的设置.例如:

// package.json
{
    "name": "test",
    "version": "0.0.1"
    "config": {
        "port": 9090
    }
}
npm config set test@0.0.1:port 8080 # <name>[@<version>]
console.log(process.env.npm_package_config_port) // 将会输出8080

常用配置

  • init.使用npm init初始化项目的时候,会使用init设置的值初始化.例如
npm config set init.author.email "***" # 设置默认的 email
  • prefix.表示全局安装的路径.更改后可指定安装目录.
  • userconfig/globalconfig表示用户配置文件和全局配置文件所在目录.
  • 一些网络相关的配置
    • strict-ssl设置为false后,表示在https请求中,不对ssl证书校验
    • proxy http的网络请求会使用此代理设置,但是如果没有设置https-proxy的情况下,https的请求也会使用proxy设置的代理.
    • https-proxy.测试后发现,不管是http还是https的源,在设置https-proxy后都不会去使用proxy.
    • ca cafile.设置CA证书的位置
    如果没有配置npm中的代理,使用环境变量https_proxy http_proxy 也能达到设置代理的目的.但是一般情况不会默认有这些环境变量,所以经常会出现浏览器能正常下载包,但是使用命令行不行,原因是浏览器会从网络设置中读取代理配置,而命令行涉及的程序是从环境变量去读代理配置.
    

npm 的常用命令

  • npm init初始化一个package.json.
    • -y/--yes.全部使用默认的配置进行初始化.
    • 在项目的根目录下配置.npm-init.js,可以自定义初始化过程.
    • 也可以在其后跟一个initializer,会使用名为create-<initializer>的初始化器,执行创建过程.例如:npm init react-app ./test将下载create-react-app,并且创建一个名为test的项目.
  • npm install 可以简写为npm i.该命令为安装一个包到项目的node_modules.
    • 如果本地有符合要求的缓存则从缓存安装,没有才会去远程下载.
    • --force.强制从远程下载从新安装.
    • -S/--save.将package安装到dependencies中,是在产品阶段需要依赖的包.
    • -D/--save-dev.将package安装到devDependencies中,是开发阶段依赖的包.
    • 当后跟本地路径时, 与npm link相似都是在node_modules创建一个软连接,但是也有不同之处.
      • npm install不会在全局的NPM中也创建软连接.
      • npm install会修改package.json.
      • npm install会触发preinstall/postinstall的hook.
  • npm update <package>根据package.json中版本设置升级包.
  • npm uninstall <package>卸载包:
    • -S/-D同install时含义一致,不过是删除.
    • --no-save卸载包,但是不删除package.json中的记录.
  • npm list --depth后跟数字可以打印出项目依赖的层级结构
  • npm link.在本地开发时,将本地的依赖连接起来.例如: A依赖B,那么在B仓库中执行npm link,会在全局生成一个软连接,然后在A仓库中npm link B即可.注意B名称应为package.json中配置的名称.
  • npm config set <key> <value>.配置npm的参数.使用npm config ls -l查看所有的配置信息.将set=>get可以查看某一项的配置信息. delete是删除一项配置.
  • npm view <package-name>可以查看一个包的信息.比如:包的下载地址,维护者信息,依赖等.
  • npm cache add <package>一般是npm内部使用,也可以使用它来手动缓存一个包.
  • npm cache verify验证缓存中包的一致性,以及收集一些垃圾数据.
  • npm cache clean --force强制清理缓存,从npm5开始必须加--force.因为npm会自动执行缓存一致性完整性的校验,所以认为不会存在缓存问题.