webpack 5 にアップグレードしました

Shingo Sasaki
スタディスト Tech Blog
11 min readJul 12, 2021

--

スタディスト 開発部 技術支援ユニットの笹木 (@s_sasaki_0529) です。

今回は、弊社が開発・運用している TeachmeBiz でも使用している、webpack を 4系から5系にアップグレードし、開発体験を向上させたお話です。

webpack

webpack 4 -> 5 のアップグレードについては、既に多くの事例がありますが、移行の手順や発生する問題というのは、プロダクトの特性や現在の構成に大きく依存するところもあるため、スタディストではどうだったかを改めて紹介致します。

TL;DR

  • webpack 4.x から webpack 5.x へのアップグレードはすんなりできた
  • ビルドコストを 1/3 削減することができた
  • Dependabot から通知される脆弱性のほとんどに対応もできた

TeachmeBiz の技術構成

TeachmeBiz のフロントエンドは、主に以下の技術構成となっています。

基本的には Vue を用いた SPA で、 Nuxt.js などのフレームワークは使用していないため、 Babel / TypeScript を含めた webpack 周りを自分たちで管理しています。

そのため、以下の webpack 関連パッケージに依存しています。(内、太字が特にアップグレードの影響を受けるパッケージ)

  • webpack 4.42.1
  • webpack-cli 3.3.11
  • webpack-dev-server 3.10.3
  • webpack-manifest-plugin 2.0.3
  • webpack-merge 4.1.3
  • hard-source-webpack-plugin 0.13.1
  • terser-webpack-plugin 3.0.2
  • moment-locales-webpack-plugin 1.1.0
  • babel-loader 8.0.6
  • css-loader 1.0.1
  • file-loader 1.1.11
  • sass-loader 7.0.3
  • style-loader 0.21.0
  • vue-loader 15.6.2
  • vue-style-loader 0.21.0
  • @storybook/vue 6.2.9

webpack 5 がリリースされたのは2020年10月でしたが、TeachmeBiz では上記の通り、多くの 関連パッケージに依存しているため、それら全てが webpack 5 をサポートするのを待つ必要がありました。

2021年06月、webpack 5 のサポートを含んだ Storybook 6.3 がリリースされ、ようやく全てのツールが対応されたことから、満を持してアップグレード作業をはじめました。

アップグレード手順

今回実施したアップグレード作業は、概ね以下の流れになっています。

  1. マイグレーションガイドを一読する
  2. 関連パッケージを最新化
  3. ビルドする
  4. エラーが出る
  5. エラーを対処する
  6. ビルドに成功するまで 4,5の繰り返し
  7. CI が通るまで 4,5 の繰り返し

本記事では割愛しますが、弊社には強力な自動テストプロセス(単体テスト、コンポーネントテスト、E2Eテスト、Visual Regression Test) が整備されているため、人力による検証は最後の最後に QA にお願いするだけで、基本的には CI での検証のみとなっています。

マイグレーションガイドを一読

パッケージを更新するとか、設定ファイルを調整するといった作業に入る前に、公式から案内されているマイグレーションガイドに目を通します。

いくつか、後述する個々のトラブルシューティングに必須の内容も含まれていますが、全体を通して以下を意識して進めることにしました。

  • パッケージは全て最新版を利用する
  • — trace-deprecation オプションなど、警告を出力する設定を常に使う

パッケージの一括アップグレード

続けて関連パッケージのバージョンを上げます。メジャーバージョンが上がったパッケージだけ抜き出しても、このぐらい上がりました。

  • webpack 4.x → 5.38.1
  • webpack-cli 3.x → 4.7.0
  • webpack-manifest-plugin 2.x → 3.x
  • webpack-merge 4.x → 5.x
  • file-loader 1.x → 6.x
  • css-loader 1.x → 5.x
  • sass-loader 7.x → 12.x
  • style-loader 0.21.x → 2.x

また、Dependabot から通知される脆弱性通知のほとんどを、これによって解消することができました。多くは開発環境に限定され、緊急度の低い脆弱性ではありましたが、こういったタイミングで潰しておくことで、本当に重要な脆弱性を見逃さずに済むと思います。

サポートブラウザを明示する

webpack 5 では、デフォルトで ES2015 を含んだランタイムコードを生成するようになりました。そのため、IE11 のようなレガシーブラウザをサポートするプロダクトの場合、明示的にサポートブラウザを指定する必要があります。

設定方法はいくつかありますが、package.json 内に browserslist の定義があれば、それを参照してくれるため、@babel/preset-env など他のツールでも参照できるように、ここで一元管理します。

"browserslist": [
"IE 11",
"last 1 chrome versions",
"last 1 ChromeAndroid versions",
"last 1 firefox versions",
"last 1 safari versions",
"iOS >= 11.4"
]

ビルドキャッシュを有効化する

webpack 5 からは、ビルド結果をキャッシュして再ビルド時に差分ビルドだけで済ませることでパフォーマンスを改善する、キャッシュ機能が本体に組み込まれています。

キャッシュ機能はビルド効率が高まる反面、キャッシュの作り方次第ではビルドに失敗するリスクも含んでいます。Teachme Biz では元々 webpack-hardsource-plugin を用いてキャッシュを使用していたため、リスクよりも効率を優先し、引き続きキャッシュを使っていきます。(development/test環境のみ)

cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
}
}

file-loader / url-loader を廃止し、Asset Modules を使用する

webpack 4 以前では、画像ファイルへの依存を解決するために、 file-loader などのローダーを別途設定する必要がありましたが、webpack 5 からは Asset Modules の仕組みを使うことで、ローダーなしで依存解決できるようになりました。

module: {
rules: {
test: /\.(png|jpe?g|gif|svg|cur|mp4)(\?.*)?$/,
type: 'asset/resource'
}
}

プラグインのインポート方法を変更

webpack-manifest-plugin / webpack-merge は、メジャーバージョンが上がり、エクスポートされるオブジェクトの構造が変化したため、インポート側も修正します。

// 旧
const merge = require('webpack-merge')
const ManifestPlugin = require('webpack-manifest-plugin')
// 新
const { merge } = require('webpack-merge')
const { WebpackManifestPlugin } = require('webpack-manifest-plugin')

webpack-dev-server の実行コマンドを変更

開発用サーバである webpack-dev-server の実行コマンドが変更され、webpack-cli を用いるようになったため、あわせて変更します。

$ yarn webpack-dev-server // 旧
$ yarn webpack-cli serve // 新

Storybook を動くようにする

Storybook 6.3 から、満を持して webpack 5 が正式にサポートされました。

本バージョンでは、Storybook 全体をビルドするための Manager Builder と、個々のストーリー(≒コンポーネント) を描画するための Preview Builder が分離され、Preview Builder として webpack 5 が使えるようになりました。(詳細は公式ブログ参照)

Preview Builder の変更は、専用パッケージを導入します。

$ yarn add -D @storybook/builder-webpack5
$ yarn add -D @storybook/manager-webpack5

builder に webpack5 を使用することを .storybook/main.js に記述します。

module.exports = {
core: {
builder: 'webpack5'
}
}

パフォーマンスを計測する

以上の工程を得て、概ね対応は完了したので、 webpack 4 から 5 へのアップグレードによるビルドコストがどう変化したのかを評価します。

Web 開発における webpack の役割は幅広く、何をもって改善したかを計測するのは難しいですが、今回は以下コマンドによるテスト用ビルドの実行時間を指標とします。

$ yarn webpack --progress --color --config webpack/test.js

webpack/test.js はテスト用の設定に一部書き換えたものですが、基本的な依存解決ルール、成果物のアウトプットルールは本番と同様になってます。

実行環境やキャッシュの有無での差異が生じないように、 CircleCI を用いたコンテナ環境上で、 webpack 4 / webpack 5 のそれぞれのブランチでのジョブを確認します。

ランダムにサンプリングし、ビルド時間の平均値をとったところ、以下のような結果になりました。

webpack 4.x: 54秒

webpack 5.x: 36秒

正直どのあたりの改修が効いたのかはわかりませんが、実に1/3 ものビルドコストが削減できました。

まとめ

本記事のように、大きな基盤改善は行わずに、 webpack のバージョンを上げるだけでも、ビルドコストを大きく削減することができました。

とはいえ、それでも 36 秒もの時間がかかっている事実から、既に TeachmeBiz は webpack で開発をするには大きくなりすぎた印象も受けます。

そこで、次回は Vite を始めとした、話題の esbuild を用いた爆速開発環境を構築し、開発環境と本番環境でそもそも基盤を分けるというアプローチにもトライしてみようと考えています。

最後に

--

--