初始化
在使用npm
之前,需要先对当前的目录进行初始化:
npm init
可以先运行npm init -h
对init的参数做更详细的了解:
% npm init -h
npm init
Usage:
npm init [--force|-f|--yes|-y|--scope]
npm init <@scope> (same as `npx <@scope>/create`)
npm init [<@scope>/]<name> (same as `npx [<@scope>/]create-<name>`)
aliases: create, innit
Run "npm help init" for more info
这里面提示npm help init
可以看到更多的操作指南,就不赘述了。
直接运行npm init
的时候,会有一步一步的提示,让开发者输入相应的信息。之后在当前目录就会产生我们熟悉的package.json
文件了。
如果不想这么麻烦,可以直接npm init -y
来跳过输入步骤。
建议在进行npm
初始化之前,先对目录做git
初始化,这样的好处是,在npm init
时,repo
项会自动填充为git
的origin
源。
// 初始化git
git init
git remote add origin git://xxxxx
总结一下:
- 在使用
npm
之前,应当使用npm init
对目录进行初始化,创建package.json
文件。 - 使用
npm init -y
来跳过初始化交互过程。 - 建议先进行
git init
,并配置好origin
源。
包管理
源配置
查看当前源设置
npm config get registry
配置新的源
npm config set registry https://registry.npm.taobao.org
指定源安装
npm --registry https://registry.npm.taobao.org install <package-name>
另,发布源默认和安装源一致,我有印象发布源可以单独配置,但用处不大,先不说了。
安装npm包
npm install <package-name>
也可以简写为
npm i <package-name>
同时安装多个包
npm i <package1> <package2> <package3> ...
依赖层级
包有两个依赖层级:项目依赖
和开发依赖
。
项目依赖的意思是,当前项目发布运行需要该包支持,那么在使用webpack
之类进行打包时,该包应当被打入到最后的package中。比如react
redux
antd
之类。此类包会标记在package.json
中的dependencies
字段。
开发依赖中的包,当前项目对其不应有运行依赖,一般都是围绕项目编译、打包、集成等做辅助工作的周边工具。比如webpack
以及各种babel
插件。此类包会标记在package.json
中的devDependencies
字段。
当然,在项目打包期间,打包工具会做treeshake
动作来晃掉未使用的包,但维护一个严谨的package依赖关系,应当是前端在管理项目时要重点关注的事情,更何况treeshake
也是有局限的。
# 安装项目依赖
npm install <package-name>
npm install <package-name> --save
npm install <package-name> -S
# 安装开发依赖
npm install <package-name> --save-dev
npm install <package-name> -D
一个真实的package.json
代码:
{
...
"dependencies": {
"@babel/core": "^7.14.6",
"@babel/plugin-transform-react-jsx": "^7.14.5",
"chalk": "^2.4.2",
"commander": "^7.2.0",
"md5": "^2.3.0",
"watch": "^1.0.2"
},
"devDependencies": {
"@types/babel__core": "^7.1.14",
"@types/md5": "^2.3.0",
"@types/watch": "^1.0.1",
"ts-node": "^10.0.0",
"typescript": "^4.3.4"
}
}
卸载包
# 卸载项目依赖
npm uninstall -S <package-name>
# 卸载开发依赖
npm uninstall -D <package-name>
# 卸载全局包
npm uninstall -g <package-name>
全局包
就像webpack-cli
或者create-react-app
之类的工具,当npm
安装其为全局时,这些包可以被当成一个工具,在bash
命令中直接运行。
# 安装全局包
npm install -g <package-name>
# 卸载全局包
npm uninstall -g <package-name>
安装全局包有几个地方需要注意:
-
安装位置是否在环境变量中做了配置?否则会无法执行。
-
一些类似nvm的工具,会对
-g
的包按node
版本进行安装,切换node
版本之后可能会找不到该包,或者出现问题1的情况。 -
Mac系统中,如果是从官网下载dmg包安装,在安装
-g
时需要sudo
,同样uninstall
也需要sudo
。建议使用Homebrew
安装node
。 -
像
webpack
ts-node
tsc
这些工具,可安装为项目的开发依赖,通过npm
运行,不必安装为-g
。这样做的好处是,项目与开发环境解耦。下面是在开发依赖中配置ts
编译器、在scripts.build
中对ts
文件进行编译的例子:{ "scripts": { "build": "tsc ./src/*.ts --esModuleInterop -d -t ES5 --lib es5 --outDir dist" }, "devDependencies": { "typescript": "^4.3.4" } }
package-lock.json
用来锁定安装包的版本号。
这个文件有可能造成一些问题,比如锁定的低版本已经从npm
源下线了。
所以当对版本没有强要求,或者是对包作者足够信赖时,我一般会把这个文件放入.gitignore
。而需要锁定的版本,直接在package.json
中写死。
关于版本号
版本号中^
字符,经测试是不会跳大版本的。
比如npm i webpack@^3
,只会安装webpack3的最新版本,并不会安装webpack4甚至webpack5。
如果想安装最新版本,使用@latest
版本号
npm i <package-name>@latest
编写指令脚本
指令 + bash
在package.json
的scripts
中,配置npm
指令和对应的运行脚本。
默认有个test
指令:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
}
我们可以试一下:
% npm run test
> testnpm@1.0.0 test
> echo "Error: no test specified" && exit 1
Error: no test specified
npm ERR! code 1
npm ERR! path /Users/kema/testnpm
npm ERR! command failed
npm ERR! command sh -c echo "Error: no test specified" && exit 1
实际上,是在bash
中执行了
echo "Error: no test specified" && exit 1
其中&&
是用顺序执行的方式来连接两个bash命令echo
和exit
。
注意最后的exit 1
,也正是这个1
,引发了后面的npm ERR! code 1
一系列报错堆栈。
我们改一下:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 0"
}
改成exit 0
之后,npm
报错就木有了:
% npm run test
> testnpm@1.0.0 test
> echo "Error: no test specified" && exit 0
Error: no test specified
所以npm run
就是在运行bash
命令。
指令 + node
我们写一个build.js
文件:
console.log('build')
在scripts
中添加build
指令:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 0",
"build": "node build.js"
}
% npm run build
> testnpm@1.0.0 build
> node build.js
build
这样,我们在build.js
中随心所欲写代码,然后用npm
来执行。无论是build.js
的业务逻辑更改,还是在scripts.build
中把js文件直接更换,我们的npm run build
这个命令是保持不变的。这对集成、运维等等都有很重要的意义。
前置和后置指令:pre | post
以build
指令为例,
prebuild
运行于build
之前postbuild
运行于build
之后
我们对scripts
做以下更改:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 0",
"build": "node build.js",
"prebuild": "echo \"Starting...\"",
"postbuild": "echo \"Done!\""
}
% npm run build
> testnpm@1.0.0 prebuild
> echo "Starting..."
Starting...
> testnpm@1.0.0 build
> node build.js
build
> testnpm@1.0.0 postbuild
> echo "Done!"
Done!
可以看到,prebuild
build
postbuild
按序先后执行。
npm工作流
我们可以通过pre/post
指令特性,来构造工作流:
"scripts": {
"pretest": "echo \"preparing for testing...\"",
"test": "echo \"testing...\" && exit 0",
"posttest": "test done!",
"build": "node build.js",
"prebuild": "npm run test",
"postbuild": "echo \"Done!\""
}
% npm run build
> testnpm@1.0.0 prebuild
> npm run test
> testnpm@1.0.0 pretest
> echo "preparing for testing..."
preparing for testing...
> testnpm@1.0.0 test
> echo "testing..." && exit 0
testing...
> testnpm@1.0.0 posttest
> test done!
> testnpm@1.0.0 build
> node build.js
build
> testnpm@1.0.0 postbuild
> echo "Done!"
Done!
上面的流程是:
npm run build
prebuild
指令运行npm run test
pretest
指令运行test
指令运行posttest
指令运行,此时prebuild
指令运行完毕。build
指令运行postbuild
指令运行
我们制造一点指令错误,看下工作流运行情况:
"test": "echo \"testing...\" && exit 1"
我们让test
指令exit 1
,引发test
指令报错,模拟单元测试没通过则终止构建的工作流。
% npm run build
> testnpm@1.0.0 prebuild
> npm run test
> testnpm@1.0.0 pretest
> echo "preparing for testing..."
preparing for testing...
> testnpm@1.0.0 test
> echo "testing..." && exit 1
testing...
npm ERR! code 1
可以看到,在运行到test
指令时,工作流中断退出,符合预期。