結尾的話説在前面。
我有時候會得出這樣的結論:原來那些我不常用的命令或工具,都是為了解決大佬們遇到的問題而存在的!
我們每天都和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。遇到困惑時,如果你發現大佬們給瞭解決方案,你會驚喜;如果沒有,你來成為大佬!
如果您覺得這篇文章還不錯,歡迎點個贊,加個關注(程序員白彬),真誠感謝您的支持。也歡迎和我直接交流,期待與您共同進步!