30歳からのプログラミング

30歳無職から独学でプログラミングを開始した人間の記録。

webpack のビルドとマークアップのパース処理

Vueのコンポーネントをwebpackでビルドする際のパフォーマンスの話。
他のライブラリでは分からない。

詳しい人にとっては常識かもしれないし、大した話ではないのだが、ここまでパフォーマンスに違いが出るのは知らなかったので、記録しておく。

webpackという文脈で「パフォーマンス」というとき、ビルドされたアプリのパフォーマンスと、ビルドそのもののパフォーマンスの2種類の話がある。この記事で扱うのは、後者。

結論から言うと、対象となるファイルのサイズよりも、コンポーネントの階層構造の複雑さが、ビルド時間に大きな影響を与える。

事例

担当中のウェブアプリがビルドに4分以上かかっており、原因を調べていたところ、とあるコンポーネントがボトルネックの1つになっていることが分かった。

そのコンポーネントは、規約の一種で、申込画面のなかでユーザーの同意を得るために表示させている。
プレーンテキストではなくマークアップされており、リスト形式や表が使われている。特にリストは何重にも入れ子になっており、それなりに複雑な構造。

規約なのでスタティックな内容であり、ユーザー毎に表示を出し分けたり、状態によって表示内容が変化したりすることはない。
他のコンポーネントとの依存関係もないので、ハードコーディングというか、用意されたマークアップファイルをそのまま貼り付けていた。

しかしこれが失敗で、このコンポーネントの存在だけでビルドが数分遅くなってしまっていた。
v-forによるリストレンダリングを使って重複する記述を省いていったところ、ビルド時間を大幅に短縮できた。

検証

どの程度の影響があるのか、実際に検証してみる。

使用したライブラリのバージョン。

  • vue@2.5.22
  • vue-loader@15.6.0
  • vue-template-compiler@2.5.22
  • webpack@4.29.0
  • webpack-cli@3.2.1

webpack.config.jsの内容は以下。vue-loader以外は何も使っていない。

const VueLoaderPlugin = require('vue-loader/lib/plugin');

module.exports = {
  module: {
    rules: [
      {
        test: /\.vue$/,
        exclude: /node_modules/,
        loader: 'vue-loader',
      },
    ],
  },
  plugins: [new VueLoaderPlugin()],
};

webpack --mode=productionを実行してビルドする。

複雑な構造のコンポーネント(ハードコーディング)

リスト構造やテーブル構造を多用し、それなりに複雑な構造を持っているコンポーネントを用意した。
かなり長いので、Gist に貼る。

Complex.vue

手元の環境だと、ビルドに52.14s.かかった。

シンプルな構造のコンポーネント

次に、先程と全く同じファイルサイズ(8128バイト)だが、構造としては単純なコンポーネントを用意した。

Simple.vue

<p>ランダムな文字列</p>が続いているだけ。

これのビルドは、3.33s.で終わった。
このことから、ファイルサイズは重要ではないということが分かる。

複雑な構造のコンポーネント(リストレンダリングによる効率化)

最後に、複雑なほうのコンポーネントを、リファクタリングする。
リストレンダリングを使うことで、マークアップの記述を減らした。

Improve.vue

面倒なので一部分しか改善を行わなかったが、それだけでも15.13s.にまでビルド時間を短縮できた。