Skip to content

[パフォーマンス] app.js バンドルサイズの削減(3.3MB → ~400KB) #2370

@masaton0216

Description

@masaton0216

背景

public/js/app.js が 3.3MB(gzip 約1MB)に肥大化しており、全ページの <head> で同期的に読み込まれるため、モバイル環境での描画遅延の主因となっています。

調査結果

現状のバンドル構成

ライブラリ 推定サイズ (min) 使用箇所
TinyMCE 7 + プラグイン群 ~800KB+ WYSIWYG エディタ(編集ページのみ)
tui-image-editor + tui-color-picker ~500KB+ WYSIWYG 内の画像編集(ごく一部のページ)
CodeMirror 6 + 4言語モード ~200KB+ テーマ編集・コード学習(管理画面の一部のみ)
Vue.js 3 ~130KB window.createApp として広く使用
lodash (全体) ~70KB window._ として公開されているが 完全に未使用
jQuery 3.7 ~90KB Bootstrap 依存、広く使用
Bootstrap 4 ~60KB コア UI
axios ~15KB AJAX通信
Tempus Dominus ~50KB 日時ピッカー
SortableJS ~45KB ドラッグ&ドロップ
dayjs ~3KB 日付フォーマット

主な問題点

  • 全ライブラリが1つのファイルに同梱され、閲覧専用ページでも TinyMCE や tui-image-editor がダウンロードされる
  • lodashwindow._ に代入されているが、プロジェクト内のどの JS/Blade ファイルからも参照されていない(完全に未使用)
  • window.Vue(Vue 2 互換)は app.js で設定されるが、全テンプレートが window.createApp(Vue 3)を使用しており未使用
  • TinyMCE は resources/views/plugins/common/wysiwyg.blade.php 経由で約13の編集テンプレートのみで使用
  • CodeMirror はテーマ編集(3画面)とコード学習プラグイン(1画面)の計4画面のみで使用

改善方針: 別エントリポイントによるバンドル分割

webpack の別エントリポイントとして、編集時のみ必要なライブラリを分離します。

動的 import() ではなく別エントリポイント方式を採用する理由:

  • TinyMCE 等はすべて window.* グローバルとして Blade テンプレートのインラインスクリプトから同期的に参照される
  • 動的 import だと Blade テンプレート側を Promise/callback に書き換える必要があり変更が大きすぎる
  • 別エントリポイントなら <script src> を条件付きで追加するだけで済む

実装手順

Step 1: lodash 削除(未使用)

ファイル: resources/js/bootstrap.js

  • window._ = require('lodash'); を削除

Step 2: window.Vue 削除(未使用)

ファイル: resources/js/app.js

  • window.Vue = require('vue').default; を削除

Step 3: resources/js/wysiwyg.js を新規作成

bootstrap.js から TinyMCE 関連と tui-image-editor(JS・CSS 両方)を移動。

含まれるもの:

  • TinyMCE 本体 + アイコン + テーマ + モデル + スキン
  • TinyMCE プラグイン群(advlist, code, link, lists, table, media, autolink, preview)
  • Connect-CMS カスタムプラグイン(image, file, translate, pdf, face, cc_template, cc_image_editor)
  • TinyMCE 日本語ロケール
  • tui-image-editor(JS + CSS)

Step 4: resources/js/codemirror.js を新規作成

bootstrap.js から CodeMirror 6 関連を移動。

含まれるもの:

  • CodeMirror コア(EditorView, placeholder, basicSetup)
  • 言語モード: javascript, css, java, php

Step 5: bootstrap.js からの削除

Step 3, 4 で移動したブロックを削除:

  • CodeMirror 関連
  • TinyMCE 関連
  • tui-image-editor 全体(JS・CSS 両方)

Step 6: webpack.mix.js にエントリポイント追加

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

Step 7: wysiwyg.blade.php に条件付き読み込みを追加

resources/views/plugins/common/wysiwyg.blade.php に wysiwyg.js と wysiwyg.css の条件付き読み込みを追加。static 変数で同一ページ内の重複読み込みを防止。

Step 8: codemirror.blade.php に条件付き読み込みを追加

resources/views/plugins/common/codemirror.blade.php に codemirror.js の条件付き読み込みを追加。

期待されるバンドルサイズ

ファイル 推定サイズ 読み込み対象
app.js(削減後) ~400KB(~120KB gzip) 全ページ
wysiwyg.js ~2.5MB(~750KB gzip) 編集ページのみ(~13テンプレート)
codemirror.js ~200KB(~60KB gzip) テーマ編集・コード学習(4ページ)

閲覧専用ページ: 3.3MB → ~400KB(約88%削減)

スコープ外(将来の改善候補)

  • app.js への defer 属性追加(<head> 内のインラインスクリプトが $() を同期参照しているため、別途対応が必要)
  • app.scss 内の TUI/WYSIWYG 関連 SCSS の分離(影響が小さいため今回は対象外)

検証方法

  1. npm run prod でビルドが成功すること
  2. public/js/app.js, wysiwyg.js, codemirror.js が生成されること
  3. public/mix-manifest.json に新しい JS/CSS ファイルが含まれること
  4. 閲覧専用ページでブラウザコンソールにエラーが出ないこと
  5. WYSIWYG エディタ(ブログ投稿画面等)で TinyMCE が正常に動作すること
  6. TinyMCE 内の画像編集(tui-image-editor)が正常に動作すること
  7. テーマ CSS/JS 編集画面で CodeMirror が正常に動作すること

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions