全網最全面的npm包管理學習

包管理工具概述

本門博客的前置知識:JavaScript、ES6、模塊化、git
本門博客的所有代碼均書寫在 nodejs 環境中,不涉及瀏覽器環境

概念

模塊(module)

通常以單個文件形式存在的功能片段,入口文件通常稱之爲入口模塊主模塊

庫(library,簡稱lib)

以一個或多個模塊組成的完整功能塊,爲開發中某一方面的問題提供完整的解決方案

包(package)

包含元數據的庫,這些元數據包括:名稱、描述、git主頁、許可證協議、作者、依賴等等

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-uBQ49ABJ-1593170155049)(assets/2019-12-13-10-39-18.png)]

背景

CommonJS 的出現,使 node 環境下的 JS 代碼可以用模塊更加細粒度的劃分。一個類、一個函數、一個對象、一個配置等等均可以作爲模塊,這種細粒度的劃分,是開發大型應用的基石。

爲了解決在開發過程中遇到的常見問題,比如加密、提供常見的工具方法、模擬數據等等,一時間,在前端社區湧現了大量的第三方庫。這些庫使用 CommonJS 標準書寫而成,非常容易使用。

然而,在下載使用這些第三方庫的時候,遇到難以處理的問題:

  • 下載過程繁瑣
    • 進入官網或 github 主頁
    • 找到並下載相應的版本
    • 拷貝到工程的目錄中
    • 如果遇到有同名的庫,需要更改名稱
  • 如果該庫需要依賴其他庫,還需要按照要求先下載其他庫
  • 開發環境中安裝的大量的庫如何在生產環境中還原,又如何區分
  • 更新一個庫極度麻煩
  • 自己開發的庫,如何在下一次開發使用

以上問題,就是包管理工具要解決的問題

前端包管理器

本門博客講解的包管理器
npm:重點
yarn:次重點
其他:瞭解

幾乎可以這樣認爲,前端所有的包管理器都是基於 npm 的,目前,npm 即是一個包管理器,也是其他包管理的基石

npm 全稱爲 node package manager,即 node 包管理器,它運行在 node 環境中,讓開發者可以用簡單的方式完成包的查找、安裝、更新、卸載、上傳等操作

npm 之所以要運行在 node 環境,而不是瀏覽器環境,根本原因是因爲瀏覽器環境無法提供下載、刪除、讀取本地文件的功能。而 node 屬於服務器環境,沒有瀏覽器的種種限制,理論上可以完全掌控運行 node 的計算機。

npm 的出現,彌補了 node 沒有包管理器的缺陷,於是很快,node 在安裝文件中內置了 npm,當開發者安裝好 node 之後,就自動安裝了 npm,不僅如此,node 環境還專門爲 npm 提供了良好的支持,使用 npm 下載的包更加方便了。

npm 由三部分組成:

  • registry:入口
    • 可以把它想象成一個龐大的數據庫
    • 第三方庫的開發者,將自己的庫按照 npm 的規範,打包上傳到數據庫中
    • 使用者通過統一的地址下載第三方包
  • 官網:https://www.npmjs.com/
    • 查詢包
    • 註冊、登錄、管理個人信息
  • CLI:command-line interface 命令行接口
    • 這一部分是本門課講解的重點
    • 安裝好 npm 後,通過 CLI 來使用 npm 的各種功能

node 和 npm 是互相成就的,node 的出現讓 npm 火了,npm 的火爆帶動了大量的第三方庫的發展,很多優秀的第三方庫打包上傳到了 npm,這些第三方庫又爲 node 帶來了大量的用戶

包的安裝

安裝(install)即下載包
由於 npm 的官方 registry 服務器位於國外,可能受網速影響導致下載緩慢或失敗。因此,安裝好 npm 之後,需要重新設置 registry 的地址爲國內地址。目前,淘寶 https://registry.npm.taobao.org 提供了國內的 registry 地址,先設置到該地址。設置方式爲npm config set registry https://registry.npm.taobao.org。設置好後,通過命令npm config get registry進行檢查

npm 安裝一個包,分爲兩種安裝方式:

  1. 本地安裝
  2. 全局安裝

本地安裝

使用命令npm install 包名npm i 包名即可完成本地安裝

本地安裝的包出現在當前目錄下的node_modules目錄中

隨着開發的進展,node_modules目錄會變得異常龐大,目錄下的內容不適合直接傳輸到生產環境,因此通常使用.gitignore文件忽略該目錄中的內容
本地安裝適用於絕大部分的包,它會在當前目錄及其子目錄中發揮作用
通常在項目的根目錄中使用本地安裝
安裝一個包的時候,npm 會自動管理依賴,它會下載該包的依賴包到node_modules目錄中
如果本地安裝的包帶有 CLI,npm 會將它的 CLI 腳本文件放置到node_modules/.bin下,使用命令npx 命令名即可調用

全局安裝

全局安裝的包放置在一個特殊的全局目錄,該目錄可以通過命令npm config get prefix查看

使用命令npm install --global 包名npm i -g 包名

重要:全局安裝的包並非所有工程可用,它僅提供全局的 CLI 工具

大部分情況下,都不需要全局安裝包,除非:

  1. 包的版本非常穩定,很少有大的更新
  2. 提供的 CLI 工具在各個工程中使用的非常頻繁
  3. CLI 工具僅爲開發環境提供支持,而非部署環境

包配置

目前遇到的問題:

  1. 拷貝工程後如何還原?
  2. 如何區分開發依賴和生產依賴?
  3. 如果自身的項目也是一個包,如何描述包的信息

以上這些問題都需要通過包的配置文件解決

配置文件

npm 將每個使用 npm 的工程本身都看作是一個包,包的信息需要通過一個名稱固定的配置文件來描述

配置文件的名稱固定爲:package.json

可以手動創建該文件,而更多的時候,是通過命令npm init創建的

配置文件中可以描述大量的信息,包括:

  • name:包的名稱,該名稱必須是英文單詞字符,支持連接符
  • version:版本
    • 版本規範:主版本號.次版本號.補丁版本號
    • 主版本號:僅當程序發生了重大變化時纔會增長,如新增了重要功能、新增了大量的API、技術架構發生了重大變化
    • 次版本號:僅當程序發生了一些小變化時纔會增長,如新增了一些小功能、新增了一些輔助型的API
    • 補丁版本號:僅當解決了一些 bug 或 進行了一些局部優化時更新,如修復了某個函數的 bug、提升了某個函數的運行效率
  • description:包的描述
  • homepage:官網地址
  • author:包的作者,必須是有效的 npm 賬戶名,書寫規範是 account <mail>,例如:zhangsan <[email protected]>,不正確的賬號和郵箱可能導致發佈包時失敗
  • repository:包的倉儲地址,通常指 git 或 svn 的地址,它是一個對象
    • type:倉儲類型,git 或 svn
    • url:地址
  • main:包的入口文件,使用包的人默認從該入口文件導入包的內容
  • keywords: 搜索關鍵字,發佈包後,可以通過該數組中的關鍵字搜索到包

使用npm init --yesnpm init -y可以在生成配置文件時自動填充默認配置

保存依賴關係

大部分時候,我們僅僅是開發項目,並不會把它打包發佈出去,儘管如此,我們仍然需要package.json文件

package.json文件最重要的作用,是記錄當前工程的依賴

  • dependencies:生產環境的依賴包
  • devDependencies:僅開發環境的依賴包

配置好依賴後,使用下面的命令即可安裝依賴

## 本地安裝所有依賴 dependencies + devDependencies
npm install
npm i

## 僅安裝生產環境的依賴 dependencies
npm install --production

這樣一來,代碼移植就不是問題了,只需要移植源代碼和package.json文件,不用移植node_modules目錄,然後在移植之後通過命令即可重新恢復安裝

爲了更加方便的添加依賴,npm支持在使用install命令時,加入一些額外的參數,用於將安裝的依賴包保存到package.json文件中

涉及的命令如下

## 安裝依賴到生產環境
npm i 包名
npm i --save 包名
npm i -S 包名

## 安裝依賴到開發環境
npm i --save-dev 包名
npm i -D 包名

自動保存的依賴版本,例如^15.1.3,這種書寫方式叫做語義版本號(semver version),具體規則後續講解

包的使用

nodejs 對 npm 支持非常良好

當使用 nodejs 導入模塊時,如果模塊路徑不是以 ./ 或 …/ 開頭,則 node 會認爲導入的模塊來自於 node_modules 目錄,例如:

var _ = require("lodash");

它首先會從當前目錄的以下位置尋找文件

node_modules/lodash.js
node_modules/lodash/入口文件

若當前目錄沒有這樣的文件,則會回溯到上級目錄按照同樣的方式查找

如果到頂級目錄都無法找到文件,則拋出錯誤

上面提到的入口文件按照以下規則確定

  1. 查看導入包的package.json文件,讀取main字段作爲入口文件
  2. 若不包含main字段,則使用index.js作爲入口文件

入口文件的規則同樣適用於自己工程中的模塊
在 node 中,還可以手動指定路徑來導入相應的文件,這種情況比較少見

語義版本

思考:如果你編寫了一個包A,依賴另外一個包B,你在編寫代碼時,包B的版本是2.4.1,你是希望使用你包的人一定要安裝包B,並且是2.4.1版本,還是希望他可以安裝更高的版本,如果你希望它安裝更高的版本,高的什麼程度呢?

回顧:版本號規則

版本規範:主版本號.次版本號.補丁版本號

  • 主版本號:僅當程序發生了重大變化時纔會增長,如新增了重要功能、新增了大量的API、技術架構發生了重大變化
  • 次版本號:僅當程序發生了一些小變化時纔會增長,如新增了一些小功能、新增了一些輔助型的API
  • 補丁版本號:僅當解決了一些 bug 或 進行了一些局部優化時更新,如修復了某個函數的 bug、提升了某個函數的運行效率

有的時候,我們希望:安裝我的依賴包的時候,次版本號和補丁版本號是可以有提升的,但是主版本號不能變化

有的時候,我們又希望:安裝我的依賴包的時候,只有補丁版本號可以提升,其他都不能提升

甚至我們希望依賴包保持固定的版本,儘管這比較少見

這樣一來,就需要在配置文件中描述清楚具體的依賴規則,而不是直接寫上版本號那麼簡單。

這種規則的描述,即語義版本

語義版本的書寫規則非常豐富,下面列出了一些常見的書寫方式

符號 描述 示例 示例描述
> 大於某個版本 >1.2.1 大於1.2.1版本
>= 大於等於某個版本 >=1.2.1 大於等於1.2.1版本
< 小於某個版本 <1.2.1 小於1.2.1版本
<= 小於等於某個版本 <=1.2.1 小於等於1.2.1版本
- 介於兩個版本之間 1.2.1 - 1.4.5 介於1.2.1和1.4.5之間
x 不固定的版本號 1.3.x 只要保證主版本號是1,次版本號是3即可
~ 補丁版本號可增 ~1.3.4 保證主版本號是1,次版本號是3,補丁版本號大於等於4
^ 此版本和補丁版本可增 ^1.3.4 保證主版本號是1,次版本號可以大於等於3,補丁版本號可以大於等於4
* 最新版本 * 始終安裝最新版本

避免還原的差異

版本依賴控制始終是一個兩難的問題

如果允許版本增加,可以讓依賴包的bug得以修復(補丁版本號),可以帶來一些意外的驚喜(次版本號),但同樣可能帶來不確定的風險(新的bug)

如果不允許版本增加,可以獲得最好的穩定性,但失去了依賴包自我優化的能力

而有的時候情況更加複雜,如果依賴包升級後,依賴也發生了變化,會有更多不確定的情況出現

基於此,npm 在安裝包的時候,會自動生成一個 package-lock.json 文件,該文件記錄了安裝包時的確切依賴關係

當移植工程時,如果移植了 package-lock.json 文件,恢復安裝時,會按照 package-lock.json 文件中的確切依賴進行安裝,最大限度的避免了差異

[擴展]npm的差異版本處理

如果兩個包依賴同一個包的不同版本,如下圖

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-SsbUzNNH-1593170553519)(assets/2019-12-17-15-17-47.png)]

面對這種情況,在 node_modules 目錄中,不會使用扁平的目錄結構,而會形成嵌套的目錄,如下圖:

├── node_modules
│   ├── a 
│   │   ├── node_modules
│   │   │   ├── c
│   │   │   |   |—— c包的文件
│   │   │── a包的文件     
│   ├── b 
│   │   ├── node_modules
│   │   │   ├── c
│   │   │   |   |—— c包的文件
│   │   │── b包的文件           

npm 腳本 (npm scripts)

在開發的過程中,我們可能會反覆使用很多的 CLI 命令,例如:

  • 啓動工程命令(node 或 一些第三方包提供的CLI命令)
  • 部署工程命令(一些第三方包提供的CLI命令)
  • 測試工程命令(一些第三方包提供的CLI命令)

這些命令紛繁複雜,根據第三方包的不同命令也會不一樣,非常難以記憶

於是,npm 非常貼心的支持了腳本,只需要在 package.json 中配置 scripts 字段,即可配置各種腳本名稱

之後,我們就可以運行簡單的指令來完成各種操作了

運行方式是 npm run 腳本名稱

不僅如此,npm 還對某些常用的腳本名稱進行了簡化,下面的腳本名稱是不需要使用run的:

  • start
  • stop
  • test

一些細節:

  • 腳本中可以省略npx
  • start腳本有默認值:node server.js

運行環境配置

我們書寫的代碼一般有三種運行環境:

  1. 開發環境
  2. 生產環境
  3. 測試環境

有的時候,我們可能需要在 node 代碼中根據不同的環境做出不同的處理

如何優雅的讓 node 知道處於什麼環境,是極其重要的

通常我們使用如下的處理方式:

node中有一個全局變量 global (可以類比瀏覽器環境的window),該變量是一個對象,對象中的所有屬性均可以直接使用

global有一個屬性是process,該屬性是一個對象,包含了當前運行node程序的計算機的很多信息,其中有一個信息是env,是一個對象,包含了計算機中所有的系統變量

通常,我們通過系統變量 NODE_ENV 的值,來判定node程序處於何種環境

有兩種方式設置 NODE_ENV 的值

  1. 永久設置
  2. 臨時設置

我們一般使用臨時設置

因此,我們可以配置 scripts 腳本,在設置好了 NODE_ENV 後啓動程序

爲了避免不同系統的設置方式的差異,可以使用第三方庫 cross-env 對環境變量進行設置

在node中讀取package.json

有的時候,我們可能在 package.json 中配置一些自定義的字段,這些字段需要在node中讀取

在node 中,可以直接導入一個json格式的文件,它會自動將其轉換爲js對象

其他npm命令 {ignore}

安裝

  1. 精確安裝最新版本
npm install --save-exact 包名 
npm install -E 包名
  1. 安裝指定版本
npm install 包名@版本號

查詢

  1. 查詢包安裝路徑
npm root [-g]
  1. 查看包信息
npm view 包名 [子信息]
## view aliases:v info show
  1. 查詢安裝包
npm list [-g] [--depth=依賴深度]
## list aliases: ls  la  ll

更新

  1. 檢查有哪些包需要更新
npm outdated
  1. 更新包
npm update [-g] [包名]
## update 別名(aliases):up、upgrade

卸載包

npm uninstall [-g] 包名
## uninstall aliases: remove, rm, r, un, unlink

npm 配置

npm的配置會對其他命令產生或多或少的影響

安裝好npm之後,最終會產生兩個配置文件,一個是用戶配置,一個是系統配置,當兩個文件的配置項有衝突的時候,用戶配置會覆蓋系統配置

通常,我們不關心具體的配置文件,而只關心最終生效的配置

通過下面的命令可以查詢目前生效的各種配置

npm config ls [-l] [--json]

另外,可以通過下面的命令操作配置

  1. 獲取某個配置項
npm config get 配置項
  1. 設置某個配置項
npm config set 配置項=
  1. 移除某個配置項
npm config delete 配置項

發佈包

準備工作

  1. 移除淘寶鏡像源
  2. 到npm官網註冊一個賬號,並完成郵箱認證
  3. 本地使用 npm cli 進行登錄
    1. 使用命令npm login登錄
    2. 使用命令npm whoami查看當前登錄的賬號
    3. 使用命令npm logout註銷
  4. 創建工程根目錄
  5. 使用npm init進行初始化

發佈

  1. 開發
  2. 確定版本
  3. 使用命令npm publish完成發佈

開源協議

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-RpbqhB7N-1593170730228)(assets/2019-12-18-16-03-02.png)]

可以通過網站 http://choosealicense.online/appendix/ 選擇協議,並複製協議內容

來源:渡一教育

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章