包管理基礎知識
在本文中,我們將詳細探討包管理器,以瞭解如何在自己的專案中使用它們——安裝專案工具依賴、保持它們最新等等。
| 預備知識 | 熟悉核心 HTML、CSS 和 JavaScript 語言。 |
|---|---|
| 目標 | 瞭解包管理器和包倉庫是什麼、為什麼需要它們,以及如何使用它們的基礎知識。 |
專案中的依賴項
依賴項是第三方軟體,可能由其他人編寫,理想情況下可以為您解決一個單一問題。一個 Web 專案可以有任意數量的依賴項,從無到多,並且您的依賴項可能包含您未明確安裝的子依賴項——您的依賴項可能有它們自己的依賴項。
您的專案可能需要的一個有用依賴項的簡單示例是一些程式碼,用於將相對日期計算為人類可讀的文字。您當然可以自己編寫這些程式碼,但很有可能其他人已經解決了這個問題——為什麼要浪費時間重新發明輪子呢?此外,可靠的第三方依賴項可能已經在許多不同情況下進行了測試,使其比您自己的解決方案更健壯且跨瀏覽器相容。
專案依賴項可以是一個完整的 JavaScript 庫或框架(例如 React 或 Vue),也可以是一個非常小的實用工具(例如我們的人類可讀日期庫),或者它可以是一個命令列工具(例如 Prettier 或 ESLint),我們在之前的文章中討論過。
如果沒有現代的構建工具,像這樣的依賴項可以透過簡單的 <script> 元素包含在您的專案中,但這可能無法直接開箱即用,您可能需要一些現代工具來將您的程式碼和依賴項捆綁在一起,以便在 Web 上釋出。捆綁包是一個通常用於指代 Web 伺服器上的單個檔案的術語,該檔案包含您的軟體的所有 JavaScript——通常儘可能地進行壓縮,以幫助減少您的軟體下載並在訪問者瀏覽器中顯示所需的時間。
此外,如果您找到一個您想替代當前工具的更好工具,或者您的依賴項釋出了您想要更新的新版本,會發生什麼?對於少數依賴項來說,這並不太痛苦,但在擁有許多依賴項的大型專案中,這種事情會變得非常難以跟蹤。使用 包管理器(例如 npm)更有意義,因為它將確保程式碼被幹淨地新增和刪除,以及許多其他優點。
包管理器到底是什麼?
我們已經遇到過 npm,但從 npm 本身來看,包管理器是一個管理專案依賴項的系統。
包管理器將提供一種安裝新依賴項(也稱為“包”)、管理包在檔案系統上的儲存位置以及提供釋出自己的包的功能的方法。
理論上,您可能不需要包管理器,您可以手動下載和儲存專案依賴項,但包管理器將無縫處理包的安裝和解除安裝。如果您不使用它,您將不得不手動處理:
- 查詢所有正確的包 JavaScript 檔案。
- 檢查它們以確保它們沒有已知的漏洞。
- 下載它們並將它們放在專案中的正確位置。
- 編寫程式碼以將包包含在您的應用程式中(這通常使用 JavaScript 模組完成,這是另一個值得閱讀和理解的主題)。
- 對所有包的子依賴項執行相同的操作,這可能有數十甚至數百個。
- 如果您想刪除包,請再次刪除所有檔案。
此外,包管理器處理重複的依賴項(這在前端開發中變得重要且常見)。
對於 npm(以及基於 JavaScript 和 Node 的包管理器),您有兩種安裝依賴項的選項。正如我們在上一篇文章中提到的,依賴項可以全域性安裝或本地安裝到您的專案中。雖然全域性安裝的優點更多,但本地安裝的優點更重要——例如程式碼可移植性和版本鎖定。
例如,如果您的專案依賴於具有特定配置的 webpack,您需要確保如果將該專案安裝到另一臺機器上或很久以後再回到該專案,配置仍然可以正常工作。如果安裝了不同版本的 webpack,它可能不相容。為了緩解這個問題,依賴項是本地安裝到專案中的。
要真正看到本地依賴項的優勢,您只需嘗試下載並執行一個現有專案——如果它能正常工作並且所有依賴項都能開箱即用,那麼您應該感謝本地依賴項使程式碼具有可移植性。
包登錄檔
為了使包管理器工作,它需要知道從哪裡安裝包,這以包登錄檔的形式出現。登錄檔是包釋出並可以從中安裝的中心位置。npm 除了是包管理器之外,也是 JavaScript 包最常用的包登錄檔的名稱。npm 登錄檔位於 npmjs.com。
npm 不是唯一的選擇。您可以管理自己的包登錄檔——像 Microsoft Azure 這樣的產品允許您建立 npm 登錄檔的代理(這樣您就可以覆蓋或鎖定某些包),GitHub 也提供包登錄檔服務,並且隨著時間的推移可能會出現更多選擇。
重要的是要確保您選擇了最適合您的登錄檔。許多專案將使用 npm,我們將在本模組的其餘示例中堅持使用它。
使用包生態系統
讓我們透過一個示例來幫助您開始使用包管理器和登錄檔來安裝命令列實用程式。
我們將使用 Vite 來建立一個空白網站。在下一篇文章中,我們將擴充套件工具鏈以包含更多工具,並向您展示如何部署網站。
Vite 提供了一些 初始化模板,其中包含所有必要的依賴項和配置,可幫助您在真實專案中快速啟動。為了演示,我們將從頭開始配置一個,使用 React 模板作為參考。
將應用設定為 npm 包
首先,建立一個新目錄來儲存我們的實驗性應用程式,放在一個容易再次找到的合理位置。我們將其命名為 npm-experiment,但您可以隨意命名。
mkdir npm-experiment
cd npm-experiment
接下來,讓我們將我們的應用初始化為一個 npm 包,這將建立一個配置檔案——package.json——它允許我們儲存我們的配置細節,以防我們以後想重新建立這個環境,甚至將包釋出到 npm 登錄檔(儘管這與我們的文章無關,因為我們正在開發一個應用程式,而不是一個可重用的庫)。
確保您在 npm-experiment 目錄內,然後鍵入以下命令:
npm init
現在會問你一些問題;npm 將根據你的回答建立一個預設的 package.json 檔案。請注意,這些都與我們的目的無關,因為它們只在你將包釋出到登錄檔並由其他人安裝和匯入時才使用。
name:識別應用的名稱。只需按 Return 鍵接受預設的npm-experiment。version:應用的起始版本號。同樣,只需按 Return 鍵接受預設的1.0.0。description:應用程式用途的簡要描述。我們在這裡省略它,但你也可以輸入任何你喜歡的內容。按 Return。entry point:這將是其他人匯入您的包時執行的 JavaScript 檔案。它對我們沒有用處,所以只需按 Return。test command、git repository和keywords:暫時按 Return 鍵將它們留空。author:專案的作者。輸入您的姓名,然後按 Return。license:釋出包所依據的許可證。暫時按 Return 鍵接受預設值。
再按一次 Return 鍵以接受這些設定。
進入你的 npm-experiment 目錄,現在你應該會找到一個 package.json 檔案。開啟它,它應該看起來像這樣:
{
"name": "npm-experiment",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Your name",
"license": "ISC"
}
我們將向 package.json 新增兩行程式碼:
"type": "module",這會使 Node 將所有.js檔案解釋為 ES 模組,而不是舊的 CommonJS 模組。這是一個普遍的好習慣。"private": true,這可以防止您意外地將包釋出到 npm 登錄檔。
將這些行新增到 "name" 下方:
{
"name": "npm-experiment",
"type": "module",
"private": true
// …
}
所以這就是定義您的包的配置檔案。目前來說這很好,讓我們繼續。
安裝 Vite
我們首先安裝 Vite,它是我們網站的構建工具。它負責將我們的 HTML、CSS 和 JavaScript 檔案打包成一個針對瀏覽器最佳化的捆綁包。
npm install --save-dev vite
完成所有操作後,再看一下您的 package.json 檔案。您會看到 npm 添加了一個新欄位 devDependencies:
{
"devDependencies": {
"vite": "^5.2.13"
}
}
這是 npm 的魔力之一——如果將來您將程式碼庫移動到另一個位置、另一臺機器上,您可以透過執行 npm install 命令重新建立相同的設定,npm 將檢視依賴項併為您安裝它們。
一個缺點是 Vite 只能在我們的 npm-experiment 應用內部使用;您無法在不同的目錄中執行它。但優點大於缺點。
請注意,我們選擇將 vite 安裝為開發依賴項。這種差異對於應用程式來說很少重要,但對於庫來說,這意味著當其他人安裝您的包時,他們不會隱式安裝 Vite。通常,對於應用程式,原始碼中匯入的任何包都是真正的依賴項,而用於開發(通常作為命令列工具)的任何包都是開發依賴項。透過刪除 --save-dev 標誌來安裝真正的依賴項。
您還會發現許多新檔案被建立:
node_modules:執行 Vite 所需的依賴檔案。npm 已為您下載了所有這些檔案。package-lock.json:這是一個鎖定檔案,儲存了複製node_modules目錄所需的精確資訊。這確保只要鎖定檔案不變,node_modules目錄在不同機器上將是相同的。
您無需擔心這些檔案,因為它們由 npm 管理。如果您使用 Git,應該將 node_modules 新增到您的 .gitignore 檔案中,但通常應該保留 package-lock.json,因為如前所述,它用於同步不同機器上的 node_modules 狀態。
設定我們的示例應用程式
無論如何,繼續設定。
在 Vite 中,index.html 檔案是核心。它定義了你應用的起點,Vite 將使用它來查詢構建應用所需的其他檔案。在你的 npm-experiment 目錄中建立一個 index.html 檔案,並給它以下內容:
<!doctype html>
<html lang="en-US">
<head>
<meta charset="UTF-8" />
<title>My test page</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
請注意,<script> 元素建立了對名為 src/main.jsx 的檔案的依賴,該檔案聲明瞭應用程式 JavaScript 邏輯的入口點。建立 src 資料夾並在該資料夾中建立 main.jsx,但暫時將其留空。
注意: type="module" 屬性很重要。它告訴瀏覽器將指令碼視為 ES 模組,這允許我們在 JavaScript 程式碼中使用 import 和 export 語法。副檔名是 .jsx,因為在下一篇文章中,我們將向其中新增 React JSX 語法。瀏覽器不理解 JSX,但 Vite 會將其轉換為常規 JavaScript,就像瀏覽器理解一樣!
使用 Vite 玩轉
現在我們將執行我們新安裝的 Vite 工具。在您的終端中,執行以下命令:
npx vite
您應該會在終端中看到類似以下內容的輸出:
VITE v5.2.13 ready in 326 ms ➜ Local: https://:5173/ ➜ Network: use --host to expose ➜ press h + enter to show help
現在我們已準備好從完整的 JavaScript 包生態系統中受益。首先,現在有一個本地 Web 伺服器在 https://:5173 執行。你暫時看不到任何東西,但很酷的是,當你對應用程式進行更改時,Vite 會自動重建並重新整理伺服器,這樣你就可以立即看到更新的效果。
您可以使用 Ctrl + C 隨時停止開發伺服器,並使用相同的命令再次啟動。如果您決定讓它繼續執行,您可以開啟一個新的終端視窗來執行其他命令。
現在來一些頁面內容。作為演示,讓我們向頁面新增一個圖表。我們將使用 plotly.js 包,這是一個數據視覺化庫。透過執行以下命令安裝它:
npm install plotly.js-dist-min
請注意我們如何在沒有 --save-dev 標誌的情況下安裝。如前所述,這是因為我們將在原始碼中實際使用此包,而不僅僅是作為命令列工具。此命令將向您的 package.json 檔案新增一個新的 "dependencies" 物件,其中包含 plotly.js-dist-min。
注意:在這裡,我們為您選擇了完成任務的包。在編寫自己的程式碼時,在查詢和安裝依賴項時請考慮以下問題:
- 我根本需要依賴項嗎?是否可以使用內建功能完成,或者是否足夠簡單可以自己編寫?
- 我具體需要做什麼?您越詳細,就越有可能找到一個完全符合您需求的包。您可以在 npm 或 Google 上搜索關鍵詞。此外,更喜歡小型包而不是大型包,因為後者在安裝、執行等方面可能會導致效能問題。
- 依賴項是否可信且維護良好?檢查上次釋出版本的時間、作者是誰以及包的每週下載量。確定包的可信度是一項需要經驗的技能,因為您必須考慮包需要更新的可能性,或者有多少人可能需要它等因素。
在 src/main.jsx 檔案中,新增以下程式碼並儲存:
import Plotly from "plotly.js-dist-min";
const root = document.getElementById("root");
Plotly.newPlot(
root,
[
{
x: [1, 2, 3, 4, 5],
y: [1, 2, 4, 8, 16],
},
],
{
margin: { t: 0 },
},
);
回到 https://:5173,您會在頁面上看到一個圖表。更改不同的數字,每次儲存檔案時都會看到圖表更新。
為生產構建我們的程式碼
然而,這段程式碼尚未準備好用於生產環境。大多數構建工具系統,包括 Vite,都有“開發模式”和“生產模式”。重要的區別在於,您在開發中使用的許多有用功能在最終站點中是不需要的,因此會為生產環境剝離掉,例如,“熱模組替換”、“即時重新載入”以及“未壓縮和註釋的原始碼”。儘管遠非詳盡,但這些是一些常見的 Web 開發功能,它們在開發階段非常有用,但在生產環境中用處不大。在生產環境中,它們只會膨脹您的站點。
現在使用 Ctrl + C 停止正在執行的 Vite 開發伺服器。
我們現在可以為我們的簡陋示例網站進行假想部署做好準備。Vite 提供了一個額外的 build 命令來生成適合釋出的打包檔案。
執行以下命令:
npx vite build
您應該會看到類似以下的輸出:
vite v5.2.13 building for production... ✓ 6 modules transformed. dist/index.html 0.32 kB │ gzip: 0.24 kB dist/assets/index-BlYAJQFz.js 3,723.18 kB │ gzip: 1,167.74 kB (!) Some chunks are larger than 500 kB after minification. Consider: - Using dynamic import() to code-split the application - Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks - Adjust chunk size limit for this warning via build.chunkSizeWarningLimit. ✓ built in 4.36s
Vite 將建立一個名為 dist 的目錄。如果您檢視它,它包含一個 index.html,它看起來與根目錄的非常相似,只是 script 的源現在被替換為 assets 資料夾的路徑。assets 資料夾包含轉換後的 JavaScript 輸出,現在已進行精簡和最佳化以用於生產。
注意:您可能會擔心警告說有一個塊太大了。這是意料之中的,因為我們正在載入一個在後臺做了很多事情的庫(想象一下自己編寫所有程式碼來繪製相同的圖表)。目前,我們不需要擔心它。
包管理器客戶端簡要指南
本教程使用 npm 安裝了 Vite 包,但如前所述,還有其他替代方案。至少值得了解它們的存在,並對這些工具之間的常見命令有一些模糊的瞭解。您已經看到了一些實際操作,但讓我們看看其他的。
列表會隨著時間增長,但在撰寫本文時,有以下主要的包管理器可用:
- npm 在 npmjs.org
- pnpm 在 pnpm.js.org
- Yarn 在 yarnpkg.com
npm 和 pnpm 從命令列角度來看是相似的——事實上,pnpm 旨在與 npm 提供的引數選項完全一致。它的不同之處在於,它使用不同的方法來下載和儲存計算機上的包,旨在減少所需的總磁碟空間。
在下面的示例中,凡是出現 npm 的地方,pnpm 都可以替換,並且命令將正常工作。
Yarn 在安裝過程方面通常被認為比 npm 更快(儘管您的體驗可能有所不同)。這對於開發人員來說很重要,因為等待依賴項安裝(並複製到計算機)可能會浪費大量時間。
然而,值得注意的是,安裝 npm 登錄檔中的包不需要 npm 包管理器。pnpm 和 Yarn 可以使用與 npm 相同的 package.json 格式,並且可以從 npm 和其他包登錄檔安裝任何包。
讓我們回顧一下您希望使用包管理器執行的常見操作。
注意:我們將演示 npm 和 Yarn 命令。它們不應在同一個專案中執行。您應該使用 npm 或 Yarn 設定專案,並始終使用該包管理器的命令。
初始化一個新專案
npm init
yarn init
如上所示,這將提示並引導您完成一系列問題以描述您的專案(名稱、許可證、描述等),然後為您生成一個 package.json,其中包含有關您的專案及其依賴項的元資訊。
安裝依賴項
npm install vite
yarn add vite
我們還在上面看到了 install 的實際應用。這會將 vite 包及其自身的依賴項直接新增到工作目錄中,位於一個名為 node_modules 的子目錄中。
預設情況下,此命令將安裝 vite 的最新版本,但您也可以控制此操作。您可以請求 vite@4,它會為您提供最新的 4.x 版本(即 4.5.3)。或者您可以嘗試 vite@^4.0.0,這意味著 4.0.0 或更高版本(與上述含義相同)。
更新依賴項
npm update
yarn upgrade
這將檢視當前已安裝的依賴項,並在可用更新的情況下,在包中指定的範圍內更新它們。
該範圍在您的 package.json 檔案中依賴項的版本中指定,例如 "vite": "^5.2.13" — 在這種情況下,插入符號 ^ 表示 5.2.13 之後(包括)的所有次要和補丁版本,直到(但不包括)6.0.0。
這是透過一個名為 semver 的系統確定的,這從文件來看可能有點複雜,但可以透過只考慮摘要資訊和版本由 MAJOR.MINOR.PATCH 表示來簡化,例如 2.0.1 是主要版本 2,補丁版本 1。嘗試 semver 值的一個極好方法是使用 semver 計算器。
需要記住的是,npm update 不會將依賴項升級到超出 package.json 中定義的範圍——要做到這一點,您需要專門安裝該版本。
更多命令
您可以線上找到有關 npm 和 yarn 的單個命令的更多資訊。同樣,pnpm 命令將與 npm 保持一致,並有一些額外的功能。
建立自己的命令
包管理器還支援建立自己的命令並從命令列執行它們。例如,之前我們使用 npx 呼叫 vite 命令來啟動 Vite 開發伺服器。我們可以建立以下命令:
npm run dev
# or yarn run dev
這將執行一個自定義指令碼,以“開發模式”啟動我們的專案。事實上,我們經常在所有專案中包含此指令碼,因為本地開發設定通常與生產環境的執行方式略有不同。
如果你嘗試在之前的測試專案中執行這個命令,它很可能會提示“dev script is missing”。這是因為 npm、Yarn(以及類似的工具)正在尋找你的 package.json 檔案的 scripts 屬性中名為 dev 的屬性。所以,讓我們在我們的 package.json 中建立一個自定義的簡寫命令——“dev”。如果你按照之前的教程操作,你的 npm-experiment 目錄中應該有一個 package.json 檔案。開啟它,它的 scripts 成員應該看起來像這樣:
{
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
}
}
更新它,使它看起來像這樣,並儲存檔案:
{
"scripts": {
"dev": "vite"
}
}
我們已經添加了一個自定義的 dev 命令作為 npm 指令碼。
現在嘗試在您的終端中執行以下命令,確保您位於 npm-experiment 目錄中:
npm run dev
這應該會啟動 Vite 並啟動與之前相同的本地開發伺服器。
請注意,我們在此處定義的指令碼不再需要 npx 字首。這是因為 npm(和 yarn)命令很聰明,它們會先搜尋專案中本地安裝的命令列工具,然後才嘗試透過傳統方法(您的計算機通常儲存和允許查詢軟體的地方)查詢它們。您可以瞭解有關 run 命令的技術細節,儘管在大多數情況下,您自己的指令碼都能正常執行。
這個特定的命令可能看起來沒有必要——npm run dev 比 npx vite 需要輸入更多的字元,但它是一種抽象。它允許我們在未來向 dev 命令新增更多工作,例如設定環境變數、生成臨時檔案等,而無需使命令複雜化。
你可以在 scripts 屬性中新增各種有助於你工作的東西。例如,Vite 在模板中推薦了這些:
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}
總結
至此,我們的包管理器之旅告一段落。我們的下一步是構建一個示例工具鏈,將我們目前所學的一切付諸實踐。