Yarn Plug'n'Play
微介紹
Yarn package manager
小歷史
2016 年 10 月 public release (v0.15.0)
發布時就改進了很多 npm 不足的地方,包含:
- 發明 lockfile
- 設置 reverse proxy 來 cache npm registry
- 安裝流程優化 (加速、local cache 等等)
- security enhancement
後來 npm 也加快腳步修正自己的不足
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
package-1@^1.0.0:
version "1.0.3"
resolved "https://registry.npmjs.org/package-1/-/package-1-1.0.3.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"
package-2@^2.0.0:
version "2.0.1"
resolved "https://registry.npmjs.org/package-2/-/package-2-2.0.1.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"
dependencies:
package-4 "^4.0.0"
package-3@^3.0.0:
version "3.1.9"
resolved "https://registry.npmjs.org/package-3/-/package-3-3.1.9.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"
dependencies:
package-4 "^4.5.0"
package-4@^4.0.0, package-4@^4.5.0:
version "4.6.3"
resolved "https://registry.npmjs.org/package-4/-/package-4-2.6.3.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"
Yarn v2
2020 年 1 月
預設使用 Plug'n'Play (PnP) 模式來安裝套件
(其實在 v1 已經支援)
這個模式預設不使用 node_modules 資料夾
node_modules 是原本放 packages 的地方
Node.js 的 module resolution algorithm
(簡化版)
1. relative paths (像是 './moduleB') ---> 你知道的
2. non-relative paths
---> 看是不是內建 module (例如 fs, path)
---> 不是的話就一層一層找 node_modules 看有沒有
├─ root
│ ├─ ...
│ └─ src
│ ├─ ...
│ └─ index.js

不使用 node_modules 這個資料夾的話
package 要找地方放
也要用別的方式找 package
為什麼 Yarn 想要拿掉它?
麻煩 1
安裝過程需要產生大量的檔案 效率不好
原先的安裝流程
(極簡化版)
- 決定要用的 package 版本
- 下載然後存進 offline mirror
- unpack 然後放到 cache
- 複製到 node_modules 資料夾
麻煩 2
Node.js 找 package 很沒效率

麻煩 3
重複的 packages

以往減少重複 packages 的方式
hoisting
即使 hoisting 還是有些時候不能避免重複

還有很多麻煩
問題在於 Node.js 的 module resolution algorithm
如果在載入 packages 的時候
可以直接知道 package 的位置
就能解決前面的問題
Node.js 不知道套件的資訊
不知道什麼時候要使用哪個版本
不知道被放在哪裡
只知道一層一層找 node_modules
Yarn 他們認為
如果 package manager
就可以決定 package 要存在哪裡
(只要它知道哪個時候用哪個版本 就可以把所有 package 放在同個地方)
可以改變安裝 packages 的方式
不用依賴 node_modules 的位置
解決原先效率差的問題
也可以提供更多功能、做更好的管理
例如可以設定
不能載入沒有列在 dependency 的 package
可以負責 module resolution
Plug'n'Play 模式下
有一個 .pnp.js 檔案
裡面包含兩個東西
- resolution tables
- resolver
用 yarn 跑 node 程式
會自動跑 .pnp.js 裡面的 method
覆寫 Node.js 載入 module 的 api
(dependency tree)
解決的麻煩
複製大量檔案、載入 package 的速度

perfect hoisting
因為 resolver 是 Yarn 寫的,不用找 node_modules

Plug'n'Play 遇到的麻煩
原先的 module resolution 方式比較寬鬆
只要在 node_modules 找得到就可以拿來用
有些套件依賴這些方式在運作
Yarn 有提供一些折衷做法
babel?
// babel.config.js
module.exports = function (api) {
api.cache(true);
const presets = [
'@babel/env',
'@babel/preset-typescript',
['@babel/preset-react', {
runtime: 'automatic',
}]];
const plugins = [
'react-hot-loader/babel',
'@babel/plugin-transform-runtime',
];
return {
presets,
plugins,
};
};
PnP 基本上會要求有列在 dependency 的 package 才可以載入
(不然他不知道要用哪個版本)
Plug'n'Play 遇到的麻煩 - 2
其它 cli 工具例如 tsc, webpack 會找不到 package



Plug'n'Play 遇到的麻煩 - 3
大家會怕

參考資料
Yarn Plug'n'Play
By luyunghsien
Yarn Plug'n'Play
- 541