lodash やめ方

みなさん、 lodash で消耗してますか？ 私は消耗しています。

なぜ lodash で消耗するかというと、とにかく思考停止でインストールされ、 `node_modules` 下で大量に重複します。サイズが大きいlodashが複数バンドルされてビルドされると、重篤なパフォーマンス上の問題を引き起こします。

https://bundlephobia.com/result?p=lodash@4.17.15

lodash には実装上の問題もあり、異様に丁寧に、そして富豪的に作られており、その結果ビルドサイズが無駄に大きいです。丁寧に作られて入るのですが、現代のフロントエンド水準や一般的なポリフィルと噛み合っていません。というわけで、常々やめたいと思っています。


ちゃんとES201xを追ってる人からすると、ほとんどの lodash のメソッドは不要に見えるはずです。本エントリは、思考停止で lodash で実装しようとする人に、ちょっと考え直しては？ と投げつける用の記事になります。


現代においては、次の選択肢があります。

- ES20xxの標準メソッドを使う
- 簡易なメソッドで代替する
- ES Modules で選択的に読み込む

## ES20xxの標準メソッドを使う

まず言語組み込みのメソッドを検討してください。ES5 の filter や reduce や map は勿論、見落とされがちな以下のメソッドを確認してください。

- Array.prototype.find
- Array.prototype.findIndex
- Array.prototype.flat
- Array.prototype.flatMap
- Array.prototype.includes
- String.prototype.includes
- String.prototype.padStart
- Object.values
- Object.assign
- Object.entries
- Object.fromEntries

本当に自分のサポートブラウザ範囲で使えるか不安な場合は、 caniuse で確認してみてください。

https://caniuse.com/#search=find

IEのような レガシーブラウザのサポートでは、 webpack 環境の場合 core-js の読み込み、 そうでない場合も埋め込みタグで UAから自動的に polyfill を出し分けしてくれる polyfill.io を検討してみてください。

- https://github.com/zloirock/core-js
- https://polyfill.io/v3/

(実際には core-js もバンドルサイズが大きい問題があるのですが、インスタンスの重複が置きづらいのと、core-js 自体の機能として選択的なローディングも可能なので、本記事ではスコープ外とします。)

## 簡易な代替メソッドを定義する

ほとんどのユースケースでは、 lodash の便利な機能は必要なく、素朴なメソッドで置換可能なはずです。

```javascript

// _.range
// n 個の配列を展開する range 関数 ※ ちゃんとした range ではないです
const range = n => [...new Array(n).keys()];
range(3); // [0, 1, 2]

// _.uniqueId
let cnt = 0;
const uniqueId = () => cnt++;
```

簡易な置き換えが可能でないか、こちらを確認してみてください

https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore


## ES Modules で選択的に読み込む

```javascript

// npm install lodash-es --save
// webpack+uglify(terser) で tree shake されて消されます
import { sortBy } from "lodash-es";

// ファイル直接指定
import sortBy from "lodash-es/sortBy";


// npm install lodash.sortby --save
// 単体モジュールでの読み込み
import sortBy from "lodash.sortby";
```


この時 `chain` は絶対に使ってはいけま せん。これはメソッドチェーンで書けるようにするものですが、内部的にすべてのファイルを読み込みます。

とはいえ、これらも内部的に 他の lodash を芋づる式に import するので、実際にはそれなりのサイズになりがちです。やめるための中間状態として、こういう形式に書き換える、と考えておくといいです。

## 実際に読み込んでるものが高いもの

自分がコードを書く範囲では、以下のメソッドで lodash を選択的に使うことが多いです。逆に言うと、これ以外では使うことは、かなり稀です。

### 特殊なコレクション操作

- sortBy
- groupBy
- difference

### 時系列系

- throttle
- debounce

(sortBy は JSに非破壊sortがないのが悪い)

## lodash がビルドから消えたか確認する

webpack-bundle-analyzer でこれらが効果を出たか確認しましょう。

https://www.npmjs.com/package/webpack-bundle-analyzer

```
npm i -g webpack-bundle-analyzer
webpack --json | webpack-bundle-analyzer
```

自分のコードから削除して、その上で未だに lodash が消えてない場合、依存パッケージが読み込んでいる可能性が高いです。

## HACK: ビルド時に yarn の resolutions で無理矢理一つにまとめる

どんなに自分が頑張ろうが、 node_modules が結局大量の lodash を引き連れてくると思います。悲しいですね。
そんな時に npm では無理矢理ですが yarn では、複数の lodash のバージョン指定を一つにまとめたりできます。

```
{
  "resolutions": {
    "lodash": "4.*",
  }
}
```


lodash 3系と4系は、主に `contains => includes` の breaking change で壊れがちなのですが、それさえ気をつければ3系への移行は簡単です。

不安な場合、 TypeScript と `@types/lodash` の導入で型チェックしながら置き換えるといいかもしれません。

## おわりに

実際には lodash が問題というより、 **フロントエンド知識のアップデートをしていない** という姿勢のシグナルとして、安易な lodash が現れることが多いです。こういう人を見つけたらこの記事を投げつけてください。

まず、lodash で、という姿勢を改めて、 lodash のメソッド一覧を読む前に https://developer.mozilla.org/ja/ を読むとか、そういう習慣を見に付けるといいと思います。