yarnがnpmと何が違うのか試してみた

  • 112
    いいね
  • 2
    コメント

はじめに

こんなpackage.jsonがあるとして

$ cat package.json
{
  "name": "sample",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "gulp-cache": "^0.4.5",
    "gulp-debug": "^2.1.2",
    "gulp-eslint": "^3.0.1",
    "gulp-size": "^2.1.0"
  }
}

yarnpackage.jsonに対応しているため、
npm install同様(デフォルトでは)node_modulesにモジュールが降ってきます。

# npm の場合
$ npm install

# yarn の場合
$ yarn
# または
$ yarn install

ちなみに上記のpackage.jsonは、以下のコマンドで、作成からモジュールのインストールまで行えます。

# package.json の作成
$ yarn init
# パッケージの追加すると、同時に取得される
$ yarn add gulp-cache gulp-debug gulp-eslint gulp-size
yarn add v0.15.1
[1/4] Resolving packages...

npm installと何が違うの?

yarn.lock なるファイルが作成されます。

yarnの場合yarn.lockが生成されます。

これはnpmの shrinkwrap に相当する機能で、
これにより依存するモジュールのバージョンを固定することができます。

# 中身はこんな感じ
$ head yarn.lock
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
acorn-jsx@^3.0.0:
  version "3.0.1"
  resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
  dependencies:
    acorn "^3.0.4"

acorn@^3.0.4:
  version "3.3.0"

じゃあ shrinkwrap との違いは何かといえば

  • デフォルトで生成されるので簡単に利用できる
  • 各モジュールがソートされており構成管理が容易

確かに人が見てもnpm-shrinkwrap.jsonと比べて記載が非常に分かりやすいです。

Yarn: A new package manager for JavaScript

モジュールのフォルダ構造が変わります。(追記あり)

node_modulesの中身を見れば一目瞭然

# npm(v2) の場合
$ ls node_modules/
gulp-cache/  gulp-debug/  gulp-eslint/  gulp-size/

# yarn の場合
$ ls node_modules/
acorn/                 generate-object-property/  normalize-package-data/
acorn-jsx/             get-stdin/                 number-is-nan/
ajv/                   glob/                      object.omit/
ajv-keywords/          globals/                   object.pick/
ansi-escapes/          globby/                    object-assign/
ansi-regex/            glogg/                     once/
..... 略

yarnは依存モジュールも含めてnode_modules直下に入るためフォルダの数が多くなります。
でもファイルサイズはyarnの方が小さいです。

# npm(v2) の場合
$ du -sh node_modules/
32M     node_modules/

# yarn の場合
$ du -sh node_modules/
20M     node_modules/

これは依存により重複しているモジュール、
例えばreadable-streamの場合だと、以下のように配置されていました。

# npm(v2) の場合
$ find -type d | grep -e readable-stream$
./node_modules/gulp-cache/node_modules/gulp-util/node_modules/multipipe/node_modules/duplexer2/node_modules/readable-stream
./node_modules/gulp-cache/node_modules/gulp-util/node_modules/through2/node_modules/readable-stream
./node_modules/gulp-cache/node_modules/readable-stream
./node_modules/gulp-debug/node_modules/gulp-util/node_modules/multipipe/node_modules/duplexer2/node_modules/readable-stream
./node_modules/gulp-debug/node_modules/through2/node_modules/readable-stream
./node_modules/gulp-eslint/node_modules/bufferstreams/node_modules/readable-stream
./node_modules/gulp-eslint/node_modules/eslint/node_modules/concat-stream/node_modules/readable-stream
./node_modules/gulp-eslint/node_modules/gulp-util/node_modules/multipipe/node_modules/duplexer2/node_modules/readable-stream
./node_modules/gulp-eslint/node_modules/gulp-util/node_modules/through2/node_modules/readable-stream
./node_modules/gulp-size/node_modules/gulp-util/node_modules/multipipe/node_modules/duplexer2/node_modules/readable-stream
./node_modules/gulp-size/node_modules/through2/node_modules/readable-stream

# yarn の場合
$ find -type d | grep -e readable-stream$
./node_modules/concat-stream/node_modules/readable-stream
./node_modules/duplexer2/node_modules/readable-stream
./node_modules/readable-stream
./node_modules/through2/node_modules/readable-stream

npmで重複されていたモジュールがyarnではきれいに整理されています。
ちなみにそれぞれ以下のバージョンのモジュールがインストールされてました。

module version
./node_modules/concat-stream/node_modules/readable-stream 2.0.6
./node_modules/duplexer2/node_modules/readable-stream 1.1.14
./node_modules/readable-stream 2.1.5
./node_modules/through2/node_modules/readable-stream 2.0.6

この中で最新の(v2.1.5)だけを利用しても問題なさそうな場合は--flatをつけて実行します。
すると以下のようにどのバージョンのモジュールを使用するかを選択できます。

$ yarn --flat
info Unable to find a suitable version for "readable-stream", please choose one by typing one of the numbers below:
  1) "readable-stream@^2.0.4, readable-stream@^2.0.2" which resolved to "2.1.5"
  2) "readable-stream@~2.0.0" which resolved to "2.0.6"
  3) "readable-stream@~1.1.9" which resolved to "1.1.14"
Answer?:

選択したバージョンがpackage.jsonresolutionsとして勝手に記録されました。

$ cat package.json
{
  "name": "sample",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "gulp-cache": "^0.4.5",
    "gulp-debug": "^2.1.2",
    "gulp-eslint": "^3.0.1",
    "gulp-size": "^2.1.0"
  },
  "resolutions": {
    "object-assign": "4.1.0",
    "readable-stream": "2.1.5",
    "vinyl": "1.2.0",
    "minimist": "1.2.0",
    "strip-bom": "3.0.0",
    "estraverse": "4.2.0",
    "acorn": "4.0.3"
  }
}
# この package.json の状態でインストール
$ yarn

これにより構造はさらにシンプル(1階層のみ)になり、フォルダサイズもちょっと小さくなりました。

$ find -type d | grep -e readable-stream$
./node_modules/readable-stream

$ du -sh node_modules/
19M     node_modules/

ただしこの場合、
バージョン指定と異なるモジュールの影響で正しく動作しない可能性があるので注意。


追記
フォルダ構成の違いは、利用しているnpmのバージョンがv2であることが原因でした。
v3を利用した場合は yarnとほぼ同じフォルダ構成が得られました。

$ npm -v
3.10.9
$ npm install
$ find -type d | grep -e readable-stream$
./node_modules/concat-stream/node_modules/readable-stream
./node_modules/duplexer2/node_modules/readable-stream
./node_modules/readable-stream
./node_modules/through2/node_modules/readable-stream

このあたり、リンク先の説明が非常に分かりやすかったです。
npm v3 Dependency Resolution

ただ私の環境ではフォルダ構成は完全に一致せず、
一部のモジュールで違いがあり、やはりyarnのほうが若干シンプルな構造になっているようでした。

# npm(v3.10.3)の場合
$ npm cache clean
$ npm install
$ find -type d | grep -e /isarray$
./node_modules/concat-stream/node_modules/isarray
./node_modules/doctrine/node_modules/isarray
./node_modules/isarray
./node_modules/isobject/node_modules/isarray
./node_modules/readable-stream/node_modules/isarray
./node_modules/through2/node_modules/isarray
$ du -sh node_modules/
21M     node_modules/

# yarn(v0.15.1)の場合
$ yarn -v
$ yarn cache clean
$ yarn install
# やはり yarn のほうがよりシンプルな構造になっている
$ find -type d | grep -e /isarray$
./node_modules/duplexer2/node_modules/isarray
./node_modules/isarray
$ du -sh node_modules/
20M     node_modules/

コメントの指摘を受け、追記させてもらいました。


まとめ

結局使えるのか?

自分のプロジェクトに適用した場合、npmに比べてモジュールのインストールがかなり早かったです。
node_modulesのファイルサイズも小さくなってまして、いつでもnpmに戻れるのでとりあえず使ってみようかなと。

方式 サイズ
npm install(v2) 378M
npm install(v3) 248M
yarn install 237M
yarn install --flat 170M

そもそも利用モジュールを減らしたい...

上記の違い以外にも、オフラインモードやネットワークのパフォーマンス改善など、
そのあたり詳しくはやっぱり公式サイトをどうぞ。