Edited at

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

More than 3 years have passed since last update.


はじめに

こんな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

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

上記の違い以外にも、オフラインモードやネットワークのパフォーマンス改善など、

そのあたり詳しくはやっぱり公式サイトをどうぞ。