结尾的话说在前面。
我有时候会得出这样的结论:原来那些我不常用的命令或工具,都是为了解决大佬们遇到的问题而存在的!
我们每天都和npm打交道,但是不少人对npm的掌握程度还停留在一个比较浅的层面(当然这也包括我)。就比如说一个用 vite 创建 app 的命令npm init @vitejs/app
,很多人就懵了,“npm init
不是用来创建package.json
文件的吗?”
同样还有npx create-react-app my-app
这样的命令,懵吗?
的确,这些命令背后还有一些我们很少关注的逻辑,虽然不难,但是我们却没有系统去了解过。
考虑到这些,最近我有系统地去学习npm,主要的学习方式是利用一些空余时间,结合我之前的npm使用经验,从npm官方文档入手去排查一些知识盲点和疑惑。顺着官方文档一块块看下来,同时对不清楚的知识点进行资料查阅和验证之后,虽然还有那么一小部分知识点我几乎没用过,但是我的确对npm有了更多的认识。最后我也是以思维导图的形式,把自己的一些学习所得简单记录下来。
经过这几天的学习,我发现我学习npm的两个大方向是npm CLI
和package.json
。
今天我想先聊聊npm CLI
,CLI就是Command Line Interface,也就是我们说的命令行接口。npm
提供了非常多的CLI
,具体可以参考npm CLI commands。
Usage: npm <command>
where <command> is one of:
access, adduser, audit, bin, bugs, c, cache, ci, cit,
clean-install, clean-install-test, completion, config,
create, ddp, dedupe, deprecate, dist-tag, docs, doctor,
edit, explore, fund, get, help, help-search, hook, i, init,
install, install-ci-test, install-test, it, link, list, ln,
login, logout, ls, org, outdated, owner, pack, ping, prefix,
profile, prune, publish, rb, rebuild, repo, restart, root,
run, run-script, s, se, search, set, shrinkwrap, star,
stars, start, stop, t, team, test, token, tst, un,
uninstall, unpublish, unstar, up, update, v, version, view,
whoami
npm <command> -h quick help on <command>
npm -l display full usage info
npm help <term> search for help on <term>
npm help npm involved overview
Specify configs in the ini-formatted file:
C:\Users\Tusi\.npmrc
or on the command line via: npm <command> --key value
Config info can be viewed via: npm help config
命令太多,就不全部解释一遍了。我筛选出了一些基础的,同时也是我见得比较多的一些命令来简单介绍下。
npm CLI常见命令
npm help
不懂就问,npm help
是个好命令。就像我用git --help
一样,对于有些比较模糊的命令,我都会用help来查一下。
npm init
在我们初始化一个 npm 包,或者说创建 package.json 文件,就需要用到npm init
。
npm init xxx
虽然之前在创建vue或者react应用时,我都用到了npm init xxx
,但我都没怎么关注npm init xxx
背后发生了什么。
比如npm init @vitejs/app
,只知道官网说它是用来创建应用的,但很少会去想到其背后是调用了npx @vitejs/create-app
,其实就是在执行一个create-app
脚本。
这也就是说,如果你想让别人通过npm init xxx
命令调用你的包,就必须提供一个create-xxx
脚本。
// 对于 yarn 来说
yarn create electron-app my-new-app
// 等价于
npx create-electron-app my-new-app
// 等价于
npm init electron-app my-new-app
npx
npx 用来运行本地或远程npm包的一个命令。比如前面提到的npx @vitejs/create-app
。
如果 npx 请求的包(比如@vitejs/create-app
)没有出现在本地项目的依赖中,npm 就会把@vitejs/create-app
安装到全局的 npm cache 目录下。
接着会执行create-app
脚本,而这个脚本需要定义在package.json
的bin
配置项下。
npm init xxx
和npx create-xxx
也是一般CLI
工具的常用套路。
npm config
npm config
是用来管理配置文件的,我们平时用的最多的是设置npm
的源。
npm config set registry https://registry.npmmirror.com
利用npm config list
,我们可以列出所有的配置项;利用get
, set
, delete
可以执行查询,设置,删除配置项的操作。
npm install / uninstall
npm install 不指定包时,会将 package.json 列出的依赖安装到 node\_modules 中,如果指定包名,则安装指定的包。主要注意:
- -g是全局安装;
- 如果指定了 --production ,或者 NODE\_ENV 是 production,就不会安装 devDependencies 中的依赖。
--save
等价于 -S,安装的依赖包信息保存到 package.json中的 dependencies,这些依赖(比如vue, react)如果有进入 bundler (比如 webpack )的 Dependency Graph(依赖关系图),会被打包到项目的构建结果中;npm install vue
会默认执行-S
的行为,但是建议显示给出-S
,给人的感觉会比较清晰。--save-dev
等价于 -D,安装的依赖包信息保存到 devDependencies 中,这些依赖一般是开发环境的工具,比如 eslint, webpack, babel之类的,这些依赖一般不会被打包工具处理到构建结果中。
但是,如果你使用npm install -D vue
安装了vue
,并且在项目中引用了vue
依赖,那么 webpack 的 Dependency Graph 中也会有vue
,最终vue
也会体现到构建结果中;
看到这里,是不是又懵逼了?不管是npm install -S vue
还是npm install -D vue
,如果项目中引用了vue
,都会把vue
打包进构建结果,那么-S
和-D
有什么区别?
注意了,webpack 不关心一个依赖是dependencies
还是devDependencies
,只要进入 webpack 的 Dependency Graph,就会打包到结果中。
所以我们不要被构建工具迷了眼,-S
和-D
影响的是npm install
,而且影响的也是有限的场景。
如果别人 install 你的包package-a
,他会顺便安装package-a
中的dependencies
,而不会去安装package-a
中的devDependencies
。
分两方面来看:
- 第一种情况:生产依赖误入开发依赖。
假设你的包package-a
通过package.json
的module
字段提供了一个ESM入口。
"module": "module-entry.js"
在module-entry.js
里面又依赖了一个包,假设是lodash-es
吧。
// module-entry.js
import { cloneDeep } from "lodash-es"
但是,你没注意你是通过npm install -D lodash-es
安装的,你在本地调试package-a
时,没有任何问题。于是,你发布了这个package-a
,同事小王安装了package-a
却发现使用时报错了(因为他不会自动安装package-a
的devDependencies
)。
- 第二种情况:开发依赖误入生产依赖。
开发环境的依赖进入了生产环境,会导致构建时多了无意义的开发依赖,打包结果变大,这常发生于开发库或组件时。
import VueAwesomeProgress from "./index.vue";
// 开发组件时,不必要的vue引入;
// 导致最终build的文件变大。
import Vue from "vue"
console.log(Vue)
VueAwesomeProgress.install = function(Vue) {
Vue.component(VueAwesomeProgress.name, VueAwesomeProgress);
};
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(VueAwesomeProgress)
}
export default VueAwesomeProgress
实质上,我们在开发一个Vue组件时,仅仅需要把vue
作为devDependencies
即可。
npm start
npm start
是一个语义化的命令。通常我们会在 scripts 中自定义 start 脚本,比如:
"start": "npm run dev"
如果没有指定自定义的 start 脚本,npm start
默认会执行:
node server.js
npm run
npm run
用来运行我们定义的scripts
,命令后直接跟脚本名称就行。在npm run
时,我们可以调用一些特殊路径下的可执行文件或脚本,这些路径包括环境变量PATH定义的路径,也包括当前项目node_modules
中的./bin
。
In addition to the shell's pre-existing PATH, npm run adds node\_modules/.bin to the PATH provided to scripts.
npm version
这个命令也是值得掌握的,从语义上看,npm version
会修改package.json
中的version
字段,用来管理包的版本号。
你可以试着运行:
npm version major/minor/patch -m "reason for upgrade"
major/minor/patch三选一,分别代表主版本/次版本/补丁版本。当然也可以传其他的版本参数,具体参考npm-version。
通常,我们还会定义一个自定义的 version 脚本,配合conventional-changelog
用来自动生成CHANGELOG.md
。
{
"scripts": {
"version": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md"
}
}
发包相关
npm login/ npm adduser
发布一个 npm 包的流程并不复杂。首先你必须通过命令行登录 npm,这用到了npm adduser
,别名是npm login
。
确保你的代理正确
有时候,考虑到国内环境,我们安装依赖时,会设置 npm 的源为淘宝镜像。但是在发布 npm 包之前,必须把源切回到 npm。
npm config set registry http://registry.npmjs.org/
npm publish
发布一个npm包,发布的界限是以 version 判断的,不能发布相同的 version。即便你只是改了一个README
,也必须修改 version 才能重新 npm publish
。
npm unpublish
与发包对应的就是移除已发布的包。你可以选择移除整个已发布的包,也可以针对性地下架某个版本。
npm pack
将package打包成 tgz 格式。举个例子,在vue-awesome-progress
目录下,运行 npm pack 将得到一个 vue-awesome-progress-1.9.1.tgz,其中 1.9.1 是取自 version 字段。
然后通过 npm install vue-awesome-progress-1.9.1.tgz 会在当前目录的 node\_moudles 目录下安装 vue-awesome-progress包及其相关依赖。
npm link
npm link
用于创建一个符号链接,类似于 Linux 软链接(ln -s
)的效果。
首先需要在待创建 link 的包目录(比如vue-awesome-progress)下运行 npm link,这会在 npm 全局文件夹下创建一个 symlink。
npm prefix -g
指向 npm 全局文件夹,我这里的值是:
PS C:\Users\Tusi> npm prefix -g
C:\Users\Tusi\AppData\Roaming\npm
npm link后,C:\Users\Tusi\AppData\Roaming\npm\node_modules\
中就有一个vue-awesome-progress
的目录了,其实是一个快捷方式。
同时,如果 vue-awesome-progress 中有配置 bin 文件,也会被 link 到全局。
要用到 vue-awesome-progress 的地方可以通过npm link vue-awesome-progress
安装它,也会安装到 node\_modules 下,不过是一个全量的 vue-awesome-progress,而非npm publish
后的 vue-awesome-progress。
个人感觉,npm link 适合在本地对两个及以上的包做调试用,这样就不用每次调试问题时,还要重新 npm run build, npm publish,省去了很多事。
写在结尾
当我们习惯了一个工具的常用功能时,很少会去想它背后发生了什么,甚至更少会去思考它还有没有其他能力。但是,当你有一定使用经验后,再去深入了解它,你会感叹:“卧槽!原来这个命令是用来解决这个问题的,大佬们果然还是考虑得全面!”
就像我开头说的那样,一个人如果想在技术领域进阶,一定要给自己提出足够多的问题,带着问题去深入。至于如何带着问题深入,我觉得最好是做一个自己的产品,可以是项目,也可以是组件,或者是 library,甚至是 framework。遇到困惑时,如果你发现大佬们给了解决方案,你会惊喜;如果没有,你来成为大佬!
如果您觉得这篇文章还不错,欢迎点个赞,加个关注(程序员白彬),真诚感谢您的支持。也欢迎和我直接交流,期待与您共同进步!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。