備受我們信賴的 NPM 上一次迎來重大更新後已經過了一段時間了。終於,在 Node 的最新版本(版本 15)中,我們等到了 NPM 的版本 7。這一版本對其內部架構進行了重大改進,並提供了一些非常有趣的新特性。
在本文中,我會介紹兩個引起我注意並激發我的想像力的新特性。第一個特性會改變我們處理所有項目依賴項的方式,而第二個特性會優化一個之前必須手動完成的流程。
當然,我指的就是 工作區(workspaces)和自動安裝 對等依賴項(peer dependencies) 的能力。是不是來興趣了?反正我心情很激動!
工作區
我最近分享了一篇文章,其中介紹了兩種 NPM 客戶端,它們都想要解決官方客戶端當前實現中的一個主要問題:npm_modules 文件夾已經成為了一個磁碟空間黑洞。
https://blog.bitsrc.io/npm-clients-that-are-better-than-the-original-cd54ed0f5fe7
這兩種選項都有自己的獨特解決方案,但總體來說它們都會將所有模塊保存在一個共享文件夾中,讓各個項目之間能夠更容易共享軟體包。而隨著 NPM 最新版本的發布和 Arborist 的引入(一個新項目,包含了負責遍歷和分析 npm_modules 文件夾內模塊目錄樹的邏輯),我們看到了官方對這種方法的回應:工作區。
請注意,這並不是一個全新的概念,其他包管理器(例如 Yarn 和 pnpm)已經有了自己的工作區實現。因此,你可以說這只是官方的一個工作區版本而已。
這是什麼?
你可以將它視為在預定義和通用上下文內的項目之間共享軟體包的一種方式。這並不是說軟體包是完全通用的,或者所有內容都要放進同一個下載位置。這個方案確實可以解決一遍又一遍地複製模塊的麻煩,還能讓你控制我們的模塊要共享給哪些項目。
創建工作區後,你就可以明確地告訴 NPM,你的程序包將存放在何處。並且由於新版客戶端可以感知工作區,因此它會正確安裝依賴項,而不會複製那些通用的依賴。
使用其他包管理器時這個功能也非常有用。例如,可以在單個 NPM 工作區中管理的多個項目之間共享一個 Bit 組件。修改共享組件時,可以用工作區從多個項目中獲得即時反饋(查看是否有哪裡出現了中斷)。
它向後兼容嗎?
可惜不行!工作區不是區區配置更改那麼簡單,它還要求你用新的方式來構造項目。因此向後兼容是做不到的,你不能運行一條 npm 命令就一次性規範化 10 個項目。但是,你可以在重新考慮所有這些項目的結構並正確更改配置之後,將這些項目的依賴項重新安裝到一個位置裡,這樣就可以對所有內容執行重複數據刪除操作了。在我看來,這確實是一項巨大的進步!
這個新特性會強制你考慮工作區的實際結構,以及不同項目之間的相互關係,這還會幫助你改善項目的內部組織架構。
考慮下面的例子
有很多介紹工作區的文章,但它們提供的示例在我看來沒什麼用,所以這裡我舉一個例子,希望能對 Node.js 開發人員更有幫助。
考慮以下文件夾結構:
基本上,我們將要處理一組 REST API,其中每個 API 的實際代碼都會放在「apis」工作區內,而通用代碼和共享包將在「core」工作區。
我們如何做到這一點呢?了解了它的工作原理後,配置過程實際上非常簡單。這裡的重點是你需要在聲明工作區的根級別(在 REST-APIS 文件夾內)定義一個 package.json 文件。準備好之後,你要做的就是在每個項目中都創建一個 package.json,並在其中聲明其所需的依賴項。
然後,你從根文件夾運行 npm install,讓 NPM 完成剩餘的工作。下面是根文件夾中的 package.json 文件:
你要做的只有這些,你當然可以定義其他屬性,但是對於這個新特性來說,「workspaces」鍵就足夠了。在其中,你可以定義(如示例所示)一個路徑列表(還有包含的通配符格式),這些路徑引用了工作區所在的文件夾。
在這些文件夾中,你只需聲明自己的 package.json 文件,而每個文件都聲明它自己的依賴項。
你可以看到,各個 API 文件夾的 JSON 文件實際上區別只有名稱和依賴項。core 文件夾也是如此,我們在其中將 Express 聲明為其主要共享依賴項:
現在,我們可以從根文件夾運行 npm install,你覺得會發生什麼呢?它將在一個通用的 node_modules 文件夾中安裝所有這些工作區所需的所有內容。因此,重複的依賴項不會佔用多份空間。
有了這些命令和文件夾結構後,你在根目錄級別的 node_modules 文件夾中安裝了所有三個模塊(及其必需的依賴項)。但是,其層次結構內的任何文件都能訪問所有這三個文件。
看看 apis/api2/ 文件夾中的這個文件:
const express = require('express')const app = express()const port = 3000const winston = require("winston")const flat = require("flat")app.get('/', (req, res) => { res.send('Hello World!')})app.listen(port, () => { console.log(`Example API (#2) listening at http://localhost:${port}`)})它用上了所有三個依賴項,而在以前它本來是找不到它們的,這太棒了!
你需要工作區嗎?
好吧,如果你正在處理的是單個項目,或者是一些互不相關的項目,那麼工作區可能對你來說並沒什麼用途。它們的需求可能會隨時改變,結果讓工作區帶來的好處煙消雲散。
但是,如果你在參與團隊中多個相關項目(也許你正在設計一個基於微服務的架構),那麼工作區可能會是你非常需要的功能。如果你有一個包含 100 個微服務的架構,所有微服務都依賴同一組模塊,那麼你可以想想這個新特性會為你節省多少磁碟空間。這個特性的用武之地就是類似這樣的場景!
PeerDependencies 自動安裝
我今天要介紹的第二大特性就是它。之前我們必須手動安裝對等依賴項,以後就用不著了。但是首先,到底什麼是對等依賴項呢?
如果你不太熟悉這個術語(以前我也不熟),這裡就簡單介紹一下:對等依賴項和普通的依賴項幾乎沒什麼區別,它們並沒有定義一個嚴格的要求,而是聲明:
你的軟體包與另一個模塊的特定版本兼容。如果該模塊已經安裝並且是正確的版本,則不要執行任何操作。如果找不到該模塊或版本存在衝突,則向開發人員顯示一條消息,警告他們這一事實,此外什麼也不做。理論上講這都沒什麼問題,但如果你要自動安裝這些依賴項,那麼當你添加兩個具有相同依賴項但版本不同的軟體包時,兩個版本就會同時安裝(其中一個位於常規的 node_modules 文件夾中,另一個作為需要它的包的子依賴項)。
這可能會導致不兼容的問題,想像一下,我們把依賴模塊 B 的模塊 A(模塊 A 又依賴 React@15)添加到依賴 React@16 的項目中。因為 A 需要版本 15,所以它也將會把版本 15 添加為依賴項,最後依賴項樹會變成這個樣子:
- React@15- A- B +- React@16也就是說你得安裝兩個不同版本的 React,僅僅因為你需要模塊 A。但現在我們有了 Arborist,它可以分析整個樹並考慮對等依賴,如果出現衝突它就會顯示對應的錯誤信息,並且中止流程。本質上來說,NPM 現在替開發人員完成了這部分工作,並幫助後者決定是否安裝這一對等依賴項。考慮到依賴管理的工作也許會浪費一整天的時間,這絕對是一個非常有用的功能。
以前你遇到過這樣的坑嗎?這個新特性是不是讓你非常激動?
NPM 版本 7 已發布,其中包含一些新特性和改進。這兩項特性尤其吸引了我的注意,我很快就去嘗試它們了。當處理具有多個共享依賴項的大型組合項目時,工作區可以從根本上改善開發人員的體驗。
對等依賴管理的改進肯定會為使用基於 NPM 的工具的 React 開發人員帶來好處,因為這一特性在這個生態系統中是非常常用的。
你喜歡這些特性嗎?還有哪些更新你覺得是很有用的?請在下方留言並分享你的看法。
延伸閱讀
https://blog.bitsrc.io/npm-7-this-is-what-i-call-an-update-de17a34ab787
Web 開發人員應避免的 5 個錯誤-InfoQ
關注我並轉發此篇文章,私信我「領取資料」,即可免費獲得InfoQ價值4999元迷你書,點擊文末「了解更多」,即可移步InfoQ官網,獲取最新資訊~