随着nodejs的发展,作为 node 自带的包管理工具的 npm 已经成为每个前端开发者必备的工具。但在日常使用中,往往只停留在npm install,遇到无法解决的问题时,只会删除node_modules 目录然后重新 install ,作为世界上最大规模的包管理系统,还有很多特性和方法需要学习。

基本概念

npm(node package manager)node的包管理工具,常见使用场景如下:

npm init

npm init 命令用来初始化一个简单的 package.json 文件,package.json 文件是用来定义一个 package 的描述文件,执行该命令后终端会依次询问 name、version,、description 等字段。

如果不想手动去输入这些字段,直接采用默认内容,可以在命令后面追加–yes参数,和一路回车效果一样。

npm init --yes

自定义 npm init 行为

npm init 命令的原理就是调用脚本,输出一个初始化的 package.json 文件。在windows当前用户的目录下创建 .npm-init.js文件

module.exports就是package.json的配置内容,使用prompt()可以获取用户输入

const desc = prompt('description?', 'A custom template...')
const type = prompt('type?', '')
const user = prompt('user?', 'test')

module.exports = {
  key: 'value',
  project: {
    type: type,
    user: user
  },
  name: prompt('name?', process.cwd().split('\\').pop()),
  version: prompt('version?', '0.1.0'),
  description: desc,
  main: 'index.js',
}

在test目录下执行npm init,得到如下package.json文件

{
  "key": "value",
  "project": {
    "type": "",
    "user": "test"
  },
  "name": "test",
  "version": "0.1.0",
  "description": "A custom template...",
  "main": "index.js"
}

除了生成 package.json,还可以执行node 脚本可以执行的任务。例如通过 fs 创建 README、.eslintrc 等项目必需文件,实现项目脚手架的作用。

依赖包安装

npm的核心功能,就是执行 npm install 从 package.json 中的 dependencies、devDependencies 将依赖包安装到当前目录的 ./node_modules 文件夹中。

默认配置下npm会从默认的源 (Registry) 中查找该包名对应的包地址,并下载安装。除了简单的指定包名,package还可以是一个指向有效包名的http、url/git、url/文件夹路径。

引用本地模块

1、在项目根目录创建要引用的模块,customerConfig文件夹,在文件夹内部新建index.js,和package.json文件,package.json文件内容如下:

{
    "name": "customerConfig",
    "main": "index.js",
    "version": "0.1.0"
}

2、在项目的package.json文件中添加依赖项

"dependencies": {
   "customerConfig": "file:./customerConfig"
}

3、执行npm install或者npm install file:./customerConfig

查看 node_modules 目录会发现多出来一个名为customerConfig,指向上层customerConfig文件夹的软链接。这是因为npm识别file:协议的url,得知这个包需要直接从文件系统中获取,会自动创建软链接到 node_modules 中,完成“安装”过程。

引用私有 git 共享 package

我们可以把被依赖的包托管在私有的git仓库中,然后将该git url保存到dependencies中,npm会直接调用系统的 git命令从git仓库拉取包的内容到node_modules中。

<protocol>://[<user>[:<password>]@]<hostname>[:<port>][:][/]<path>[#<commit-ish> | #semver:<semver>]

例如:

git+ssh://git@github.com:npm/npm.git#v1.0.27
git+ssh://git@github.com:npm/npm#semver:^5.0
git+https://isaacs@github.com/npm/npm.git
git://github.com/npm/npm.git#v1.0.27

npm script的使用

npm 可以在项目package.json里面自定义脚本命令

// package.json文件

{
  "script":{
    "dev": "webpack -w"
  }
}

package.json文件中的字段script的每一个属性都是一个自定义的脚本命令。

在命令行中可以通过执行npm run dev来执行这段脚本。

执行原理

使用npm run script执行脚本的时候都会创建一个shell,然后在shell中执行指定的脚本。

这个shell会将当前项目的可执行依赖目录(即node_modules/.bin)添加到环境变量path中,当执行之后之后再恢复原样。就是说脚本命令中的依赖名会直接找到node_modules/.bin下面的对应脚本,而不需要加上路径。

传参

向 npm 脚本传入参数,要使用–标明。

npm run test -- --grep="pattern"

可以将 –grep=”pattern” 参数传给 test 命令

执行顺序

一个npm脚本可以执行多个任务,这些任务之间可以指定不同的执行顺序。

// '&' 并行执行顺序,同时执行
"dev":"node test.js & webpack"

// '&&'继发顺序,执行前面之后才可以执行后面
"dev":"node test.js && webpack"

顺序钩子

npm脚本自带两个顺序钩子,’pre’ 和 ‘post’

"predev":"node test_one.js",
"dev":"node test_two.js",
"postdev":"node test_three.js"

当执行npm run dev的时候默认就会执行

npm run predev && npm run dev && npm run postdev

可以缩写的脚本命令

npm start === npm run start
npm stop === npm run stop
npm test === npm run test
npm restart === npm run stop && npm run restart && npm run start

使用package.json内部变量

通过npm_package_前缀,npm脚本可以拿到npm的内部变量

// package.json:
{
  "name":"develop",
  "script":"node test.js"
}

test.js:
console.log(process.env.npm_package_name) //develop