Yarn 團隊在春節前公佈了 Yarn 2.0 的規劃。其中提到了一個以前沒據說過的名詞 「PnP」。發現 Yarn 的這個功能早在 18 年 9 月份就被提出並實現了。因而花了一些時間瞭解了一下它的工做原理以及解決的問題並整理除了本篇文章。html
Yarn 團隊開發 PnP 特性最直接的緣由就是現有的依賴管理方式效率過低。引用依賴時慢,安裝依賴時也慢。node
先說說 Node 在處理依賴引用時的邏輯,這個流程會有以下兩種狀況:react
require()
調用的參數是一個核心模塊(例如 "fs"、"path"等)或者是一個本地相對路徑(例如 ./module-a.js
或 /my-li/module-b.js
),那麼 Node 會直接使用對應的文件。node_modules
的目錄:
node_modules
,若是沒有則到父目錄查找,以此類推直到系統根目錄。node_modules
目錄以後,再在該目錄中尋找名爲 moduleName.js
的文件或是名爲 moduleName
的子目錄。此處旨在說明問題,對 Node 內部模塊解析邏輯作了簡化描述git
可見 Node 在解析依賴時須要進行大量的文件 I/O 操做,效率並不高。github
再來看看安裝依賴時發生了什麼,現階段 yarn install
操做會執行如下 4 個步驟:npm
node_modules
目錄其中第 4 步一樣涉及大量的文件 I/O,致使安裝依賴時效率不高(尤爲是在 CI 環境,每次都須要安裝所有依賴)。json
Facebook 的工程師受夠了這些問題決定尋找一個能完全解決問題同時還能夠與現有生態兼容的解決方案。這即是 Plug'n'Play 特性,簡稱 PnP。它已在 Facebook 內部測試了一段時間,如今 Yarn 團隊決定與社區分享並共同優化該方案。api
PnP 的具體工做原理是,做爲把依賴從緩存拷貝到 node_modules
的替代方案,Yarn 會維護一張靜態映射表,該表中包含了如下信息:緩存
這個映射表在 Yarn 的 PnP 實現中對應項目目錄中的 .pnp.js
文件。bash
這個 .pnp.js
文件是如何生成,Yarn 又是如何利用它的呢?
在安裝依賴時,在第 3 步完成以後,Yarn 並不會拷貝依賴到 node_modules
目錄,而是會在 .pnp.js
中記錄下該依賴在緩存中的具體位置。這樣就避免了大量的 I/O 操做同時項目目錄也不會有 node_modules
目錄生成。
同時 .pnp.js
還包含了一個特殊的 resolver,Yarn 會利用這個特殊的 resolver 來處理 require()
請求,該 resolver 會根據 .pnp.js
文件中包含的靜態映射表直接肯定依賴在文件系統中的具體位置,從而避免了現有實如今處理依賴引用時的 I/O 操做。
從 PnP 的實現方案能夠看出,同一個系統上不一樣項目引用的相同依賴的相同版本實際都是指向的緩存中的同一個目錄。這帶來了幾個最直觀的好處:
首先你須要 Yarn 1.12+ 版本。而後根據你的具體場景能夠選擇:
create-react-app
建立項目時開啓 PnPcreate-react-app
已經集成了對 PnP 的支持。只需在建立項目時添加 --use-pnp
參數便可。
npx create-react-app testapp --use-pnp
複製代碼
只需在項目中執行:
yarn --pnp
複製代碼
便可開啓 PnP 特性。
pkg.installConfig
字段在項目中開啓 PnP 特性後,Yarn 會在 package.json
文件中建立一個 installConfig
字段:
{
"installConfig": {
"pnp": true
}
}
複製代碼
只要 installConfig.pnp
的值是一個真值且當前版本的 Yarn 支持,PnP 特性就會被啓用。
npm script
或是運行 .js
文件因爲在開啓了 PnP 的項目中再也不有 node_modules
目錄,全部的依賴引用都必須由 .pnp.js
中的 resolver 處理。所以不管是執行 script 仍是用 node
直接執行一個 JS 文件,都必須經由 Yarn 處理。必須經過 yarn run
或是 yarn node
執行。
在開發過程當中咱們有時會直接修改 node_modules
目錄下的依賴來調試。但在 PnP 模式下,因爲依賴都指向了全局緩存,咱們再也不能夠直接修改這些依賴。
針對這種場景,Yarn 提供了 yarn unplug packageName
來將某個指定依賴拷貝到項目中的 .pnp/unplugged
目錄下。以後 .pnp.js
中的 resolver 就會自動加載這個 unplug 的版本。
調試完畢後,再執行 yarn unplug --clear packageName
可移除本地 .pnp/unplugged
中的對應依賴。
目前 PnP 仍是一個相對比較新的特性,你們能夠嘗試在本地開發環境中啓用 PnP 來感覺一下它帶來的全新體驗。遇到問題能夠及時反饋到 Yarn 的 issue 列表中。
參考連接:
更多文章,請關注咱們團隊的公衆號:全棧探索