Yarn Plug'n'Play

微介紹

Yarn package manager

小歷史

2016 年 10 月 public release (v0.15.0)

發布時就改進了很多 npm 不足的地方,包含:

後來 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

安裝過程需要產生大量的檔案 效率不好

原先的安裝流程

(極簡化版)

  1. 決定要用的 package 版本
  2. 下載然後存進 offline mirror
  3. unpack 然後放到 cache
  4. 複製到 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