npm相關知識整理

語義化版本

  • major: 重大變化,不兼容老版本

  • minor: 新增功能,兼容老版本

  • patch: 修復bug,兼容老版本

依賴版本號

  • * 匹配最新版本的依賴

  • ^ 匹配最近的大版本依賴,比如^1.2.3,會匹配所有1.x.x的包,不包括2.0.0

  • ~ 會匹配最近的小版本依賴,比如~1.2.3,會匹配所有1.2.x版本,不包括1.3.0

package入口

如果使用import對庫進行導入,會優先尋找module欄位引入,然後才是main欄位。

{
  name: 'midash',
  main: './dist/index.js', // es module入口
  module: './dist/index.mjs' // commonjs入口
}

package-lock

鎖定規則

  • 當package-lock.json中的依賴鎖定版本符合package.json中的版本號範圍時,將以package-lock.json鎖死版本號為主。

  • 當package-lock.json中的依賴鎖定版本不符合package.json中的版本號範圍時,將會安裝符合package.json版本號範圍的最新版本號依賴,並重寫 package-lock.json文件

要不要提交package-lock

  • 對於項目開發而言,有必要。當有了lock文件時,每一個依賴的版本號都被鎖死在了lock文件,每次依賴安裝的版本號都從lock文件中進行獲取,避免了不可測的依賴風險。

  • 對於庫開發而言,也有必要。雖然項目中所有依賴都會根據項目lockfile鎖死而並不會依照庫依賴的lockfile,但是為了貢獻者能很容易地將項目跑起來,devDependencies必須在庫的lockfile中鎖定。

npm腳本原理

  • 每當執行npm run xx,就會自動新建一個Shell,在這個Shell裡面執行指定的腳本命令。因此,只要是Shell可以運行的命令,一般是Bash,就可以寫在npm腳本裡面。
  • npm run新建的這個Shell,會將當前目錄的node_modules/.bin子目錄加入PATH變數,執行結束後,再將PATH變數恢復原樣,這也就是為什麼npm run指向的命令能夠被執行的原因。

npm腳本執行

{
  "scripts": {
    "bx": "npm run script1.js & npm run script2.js", // 並行執行(同時執行)
    "abc": "npm run script1.js && npm run script2.js", // 繼發執行(即只有前一個任務成功,才執行下一個任務)
  }
}

Windows下不支援這2種方式,可以通過安裝npm-run-all、Concurrently模組解決。

npm常用別名

npm add -> npm install
npm create -> npm init

npm發布

  1. 第一步,在npm官網進行註冊一個帳號。
  2. 第二步,執行 npm login,輸入註冊的帳號密碼完成登錄。
  3. 第三步,npm i -y 初始化 package 文件,填入最基本的3個欄位:name,version 和 main 欄位,其中main欄位應該是打包後的程式碼。
  4. 第四步,在包目錄執行npm publish

npm變數

通過npm_package_前綴,js腳本可以通過process.env對象拿到package.json裡面的欄位。比如,下面這個package.json:

{
  "name": "foo", 
  "version": "1.2.5",
  "scripts": {
    "view": "node view.js"
  }
}
// view.js
console.log(process.env.npm_package_name); // foo
console.log(process.env.npm_package_version); // 1.2.5

npm_package_前綴也支援嵌套的package.json欄位

// view.js
console.log(process.env.npm_package_scripts_view); // node view.js

npm依賴

  • dependencies 生產依賴

  • devDependencies 開發依賴

安裝命令

npm i --save xxx // 生產依賴
npm i --save-dev // 開發依賴

npm i -S xxx // 生產依賴(簡寫)
npm i -D xxx // 開發依賴(簡寫)

是否要嚴格區分生產和開發?

  • 當進行項目開發時,並無必要。因為打包時,依靠的是Webpack/Rollup對程式碼進行模組依賴分析,與該模組是否在dep/devDep並無關係,只要在 node_modules上能夠找到該依賴即可。

  • 當進行庫開發時,有必要。因為當在項目中安裝一個依賴庫時,只有該依賴的dependencies會被安裝到node_modules中,devDependencies不會,因此這裡如果不區分,會導致開發項目報錯受影響。

npm鉤子

npm鉤子,也稱npm生命周期。npm默認提供pre和post兩個鉤子,當我們執行任意npm run腳本時,都將自動觸發pre/post的鉤子但是這2個鉤子的內容需要由你自定義。

{
  "scripts": {
    "preabc": "xxx",
    "abc": "xxx",
    "postabc": "xxx"
  }
}

當你執行npm run abc時,會自動按照下面的順序執行

npm run preabc -> npm run abc -> npm run postabc

一些常用npm腳本,如:publish、install、uninstall、version、test、stop、start和restart,同樣支援自定義這2個鉤子。

npm restart

npm restart是一個複合命令,實際上會執行三個腳本命令:stop、restart、start。具體的執行順序如下:

prerestart
prestop
stop
poststop
restart
prestart
start
poststart
postrestart

npm publish

npm publish發包的生命周期比較複雜,當執行npm publish命令,將自動執行以下腳本:

prepublishOnly: 最重要的一個生命周期,如果你需要在發包之前自動做一些事情,如測試、構建等,可以在這裡完成。
prepack
prepare
postpack
publish
postpublish

prepare鉤子

一個最常用的生命周期鉤子,它的執行時機:

  • npm install 之後自動執行

  • npm publish 之前自動執行

常用的git hook工具husky通常就被放置在這個鉤子里執行

{
  prepare: "husky install";
}

npm_lifecycle_event變數

npm提供一個npm_lifecycle_event變數,返回當前正在運行的腳本名稱,比如pretest、test、posttest等等。所以,可以利用這個變數,在同一個腳本文件裡面,為不同的npm scripts命令編寫程式碼。

const TARGET = process.env.npm_lifecycle_event;

if (TARGET === 'test') {
  console.log(`Running the test task!`);
}

if (TARGET === 'pretest') {
  console.log(`Running the pretest task!`);
}

if (TARGET === 'posttest') {
  console.log(`Running the posttest task!`);
}

npx命令

npm 從5.2版開始,增加了npx命令,可以提升開發者使用包內提供的命令行工具的體驗,它有很多用處。

方便調用項目安裝的模組

通常我們安裝完npm依賴或腳手架後,需要將其寫入package.json的scripts欄位裡面,運行npm run xxx才能執行,npx就是想解決這個問題,利用npx,我們可以直接調用運行。

"scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "build-bundle": "vue-cli-service build --target lib --name t3-ui ./src/components/index.js",
    "lint": "vue-cli-service lint"
}

我們用npx命令的話, 就不需要在寫scripts裡面的內容, 直接在控制台執行npx vue-cli-service serve,就會直接尋找本地依賴並執行。

避免全局安裝模組

運行npx命令後,npx會將依賴下載到一個臨時目錄,使用以後再刪除

npx [email protected] main.js -o ./dist/main.js // 臨時下載壓縮包,執行壓縮
npx http-server // 臨時在當前目錄,安裝並運行起本地伺服器

使用不同版本的node

npx -p node@6 npm run serve

執行邏輯如下:

  • npx下載node@6版本
  • 將執行的node版本切換為node@6版本
  • 使用node@6執行npm run serve
  • 命令執行完畢後,刪除下載包,最終不會改變本地的使用版本

npx命令執行邏輯

先自動查找當前依賴包中的可執行文件,如果找不到,就會去PATH里找,如果依然找不到,就會幫你臨時安裝,執行完後再刪除包。

npm init命令

命令用法:

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>`)

npm init xxx時,會自動轉換成如下npx命令:

npm init foo -> npx create-foo
npm init @usr/foo -> npx @usr/create-foo
npm init @usr -> npx @usr/create

以vue3官方安裝為例,命令實際效果如下:

// 實際安裝的是create-vue@latest包
npm init vue@latest -> npx create-vue@latest

npm exec命令和npx作用相同,區別僅在於後者會將其後面的標誌和選項都會優先設置為位置參數,具體可對照npm文檔。

npm link命令

簡而言之,就是可以將你本地的npm開發包當作是已經npm install好後的包使用了,用來在本地項目和本地npm模組之間建立連接,方便關聯到項目中進行修改/調試。

項目和本地npm模組在同一個目錄下,可以使用相對路徑進行link

npm link ../module   // 然後,你本地項目的node_modules就出現了該module的軟鏈接

1.注入全局link

cd modulePath
npm link // 然後,全局node_modules目錄下就出現該module的軟鏈接

2.引入全局link

cd projectPath
npm link module

解除局部link

cd projectPath
npm unlink module

解除全局link

cd modulePath
npm unlink

以vite為例

如果你迫不及待想要體驗最新的功能,可以自行克隆vite倉庫,然後到本地機器上然後自行將其鏈接

// 第1步,克隆並進入目錄
git clone //github.com/vitejs/vite.git
cd vite

// 第2步,安裝vite作為npm開發包的依賴,然後打包出開發者需要的vite
pnpm install
cd packages/vite
npm run build

// 第3步,全局注入link
npm link

// 第4步,進入本地項目,引入link,這個時候就相當於全局安裝了vite,類似於執行了vite官方安裝命令:npm create vite@latest
pnpm link --global vite

// 第5步,package文件的script內dev寫入vite命令
dev: 'vite'

// 第6步,執行npm命令
npm run dev

實際測試發現,使用link後,第5和第6步是多餘的,本地可以直接在控制台使用vite命令,不管你是全局link的,還是局部link的,都可以運行。

參考文章

Tags: