Laravel Mixを使ったフロントエンドビルドプロセス最適化:遅い・重い・壊れるを減らす実務設定

Laravel Mixを使ったフロントエンドビルドプロセス最適化:遅い・重い・壊れるを減らす実務設定

Laravel Mixは「webpackの難しさを隠して、Laravel向けのビルドを素早く組める」構成になっている。一方で、プロジェクトが大きくなるとビルド時間の増加、CSS/JSの肥大、キャッシュが効かない、環境差でエラーが出るなどの問題が目立つ。最適化は、(1) 開発時は速く回す、(2) 本番は小さく固める、(3) 依存関係・Node環境差の事故を潰す、の3本柱で進めると失敗しにくい。

発生条件:Laravel Mixが遅くなる・壊れやすくなる典型パターン

・開発環境でも毎回minifyされていてビルドが遅い(production設定の混在)
・source mapを本番で生成して配布物が重い
・vendor(依存JS)と自作JSが分離されておらず、更新のたびに巨大バンドルが作り直される
・不要なCSS(特にTailwind/Bootstrapの未使用分)が残り続ける
・画像最適化が無く、転送量でUXが悪化する
・Nodeのバージョン差でWebpackが落ちる(OpenSSL関連など)
・CIでメモリ不足になり、ビルドが途中で落ちる

まず押さえる:Mixの基本構造(webpack.mix.jsとnpm scripts)

Mixはwebpack.mix.jsが中心で、ここに「入力→変換→出力」を書く。npm scriptsは dev / watch / prod を分けて、開発時と本番で処理を切り替えるのが基本。

// package.json(例)
{
  "private": true,
  "scripts": {
    "dev": "npm run development",
    "development": "mix",
    "watch": "mix watch",
    "hot": "mix watch --hot",
    "prod": "npm run production",
    "production": "mix --production"
  },
  "devDependencies": {
    "laravel-mix": "^6.0.0",
    "sass": "^1.70.0",
    "sass-loader": "^12.0.0"
  }
}

開発ビルドを速くする:sourceMaps・通知・watchの最適化

開発時は「毎回のビルドを短くする」ことが最優先。source mapは必要最低限にし、watchの監視範囲を絞ると体感が変わる。

// webpack.mix.js(開発の体感改善例)
const mix = require('laravel-mix');

mix.js('resources/js/app.js', 'public/js')
   .sass('resources/sass/app.scss', 'public/css');

// 開発時だけ source map
if (!mix.inProduction()) {
  mix.sourceMaps();
}

// OS通知が重い環境では無効化
mix.disableNotifications();

・WSL/VM/ネットワークドライブ上のファイルはwatchが遅くなりやすい
・watchが不安定なら、HMR(hot)ではなく通常watchに寄せる方が安定するケースがある

本番を小さくする:versioning(キャッシュ)とminify(圧縮)の基本

本番は「キャッシュが効くファイル名」と「圧縮」をセットで入れる。特にversioningは配信最適化の土台になる。

// Blade(例)
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
<script src="{{ mix('js/app.js') }}" defer></script>

Blade側は mix() を使うのが前提。

// Blade(例)
<link rel="stylesheet" href="{{ mix('css/app.css') }}">
<script src="{{ mix('js/app.js') }}" defer></script>

依存JSの肥大を止める:vendor分離・chunk最適化

依存が増えるほどapp.jsが巨大化し、少しの修正で全体再ビルドになりやすい。vendorの分離とchunk戦略を入れると「更新差分が小さくなる」。

// webpack.mix.js(vendor分離の例)
const mix = require('laravel-mix');

mix.js('resources/js/app.js', 'public/js')
   .extract([
     'axios',
     'lodash'
   ])
   .sass('resources/sass/app.scss', 'public/css');

if (mix.inProduction()) {
  mix.version();
}

・extractは依存が多いほど効く一方、構成によっては差が出にくい場合もある
・chunkの切り方を増やしすぎるとHTTPリクエストが増えるので、目的(更新頻度・サイズ)に合わせる

CSSが重い問題:未使用CSSの削減(Purge系)と設計ルール

CSS肥大の発生条件は「全部入りのCSSをそのまま出している」こと。特にユーティリティ系(Tailwind)や大規模UIで顕著になる。対策は2段階。
(1) そもそもページ単位のCSSを分割する
(2) 本番で未使用CSSを削る(Purge系プラグイン)

Purge系は導入手順がプロジェクトにより変わるため、まずは「CSS分割」だけでも効果が出る。

// webpack.mix.js(ページ別CSS出力例)
const mix = require('laravel-mix');

mix.sass('resources/sass/app.scss', 'public/css')
   .sass('resources/sass/admin.scss', 'public/css');

mix.js('resources/js/app.js', 'public/js')
   .js('resources/js/admin.js', 'public/js');

if (mix.inProduction()) {
  mix.version();
}

画像・フォント最適化:ビルド外の“配信コスト”も落とす

JS/CSSを最適化しても、画像が重いと体感が改善しない。発生条件は「元画像をそのままpublicに置いている」こと。
・WebP/AVIFなど軽量形式へ
・大きい画像のリサイズ(最大幅を決める)
・フォントは必要ウェイトだけに絞る
・遅延読み込み(lazy)で初期表示を軽くする

Mixの範囲外でやるケースも多いが、「配信サイズの削減」という目的で同じラインに置いておくと管理しやすい。

ビルドの安定化:Node/依存関係の固定とCI向けメモリ対策

発生条件:ローカルは通るのにCIで落ちる、メンバー間でNodeが違いエラーが揺れる。
対策は「Nodeバージョン固定」「lockファイル厳守」「メモリ不足対策」。

// .nvmrc(例:チームでNodeを揃える)
18.20.3

// CIやローカルでメモリ不足になる場合の例(Linux/macOS想定)
export NODE_OPTIONS="--max_old_space_size=4096"
npm run production

よくあるエラーと対処(発生条件つき)

1) 「mix: command not found」「Cannot find module ‘laravel-mix’」
発生条件:node_modules未作成、npm install未実行、依存が壊れている。
対処:node_modulesを作り直す。

rm -rf node_modules package-lock.json
npm install
npm run dev

2) 「error:0308010C:digital envelope routines::unsupported」などOpenSSL系
発生条件:Nodeのバージョンが新しすぎて、Webpack側が対応しきれていない組み合わせ。
対処:NodeをLTSへ揃える、または互換オプションを付ける(暫定)。

// 暫定回避(環境により)
export NODE_OPTIONS="--openssl-legacy-provider"
npm run dev

3) 「JavaScript heap out of memory」
発生条件:大型プロジェクト、CIメモリが少ない、ソースマップやminifyで負荷が増える。
対処:メモリ増加、source mapの見直し、分割ビルド。

export NODE_OPTIONS="--max_old_space_size=4096"
npm run production

4) 変更してもCSS/JSが反映されない
発生条件:キャッシュバスター無し、mix-manifest.json未更新、ブラウザキャッシュが強い。
対処:本番はmix.versionを入れる、Bladeはmix()で参照する。

実践サンプル:開発は速く、本番は小さく、キャッシュが効く構成

下記は「最低限の最適化」をまとめた実務向けの雛形。

// webpack.mix.js(雛形)
const mix = require('laravel-mix');

mix.setPublicPath('public');

mix.js('resources/js/app.js', 'public/js')
   .sass('resources/sass/app.scss', 'public/css');

if (!mix.inProduction()) {
  mix.sourceMaps();
  mix.disableNotifications();
} else {
  mix.version();
}

まとめ:Mix最適化の優先順位

・開発:source mapは必要最低限、watchを安定させる、不要な処理を切る
・本番:versioningでキャッシュ、圧縮、不要CSS削減の設計(分割+Purge系)
・依存:vendorを意識して肥大を止める
・安定:Nodeと依存関係を固定し、CIのメモリ不足を潰す
・配信:画像とフォントの最適化も同じレイヤで管理する