0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Laravel + Inertia + Vue】ライフサイクルごとに発火するイベントを全て紐解く!

Last updated at Posted at 2025-12-19

後なに喋ってないかな...ネタ切れも近い...:frowning2:

と思ったのですが、まだイベントのサイクルについて記事を書いていなかったですね。

定義されているイベント

Inertia では以下の router イベントが埋め込まれています。

  • before
  • start
  • progress
  • success
  • flash
  • error
  • invalid
  • exception
  • finish
  • navigate
  • prefetching
  • prefetched

上記のイベント router.on()document.addEventlistener() で傍受することができます。

// これらはすべて同じ意味
import { router } from '@inertiajs/vue3'

// (1) グローバルに傍受
router.on('start', (e) => {
    console.log(e)
})

// (2) 各処理でハンドリングを差し込む
rouer.visit('<url>', {
  onStart: (e) => {
    console.log(e)
  }
})

// (3) (1) は document からも傍受できる
document.addEventListener('inertia:start', (e) => {
    console.log(e);
})

これらは Vue やブラウザのサイクルとどのように関わっているのでしょうか。

大事なイベント

長くなるので先にまとめます!
大事なものは大きく二つ!

  • ページ遷移が起きた時は navigate が発火
  • 通信が発生したときは before - start - success/error - finish の順で発火

表にするとこのような形です。

シナリオ [window]
load
[window]
popstate
[vue]
mounted
inertia:
navigate
inertia:
start
初回訪問
ページ遷移
(Link)
「戻る」「進む」
フォームの送信
(preserveState: true)
ページの更新
(preserveState: true)
ページの更新
(preserveState: false)

挙動を確認してみる

では物理的にイベントを console.log でキャプチャして、挙動を確認します。
以下のプラグインを作成して app.ts に紐づけます。

結局愚直に確認するのがかんたん!
明らかに邪魔になるイベントはコメントアウトしています。

参考記事はこちら。

resources/js/listenerPlugin.ts
import { App } from 'vue'

export const listenerPlugin = {
  install(app: App) {
    // (1) Window で傍受するイベント
    const windowEvents = [
      'afterprint',
      'appinstalled',
      'beforeinstallprompt',
      'beforeprint',
      'beforeunload',
      'blur',
      // 'devicemotion', 邪魔
      // 'deviceorientation', 邪魔
      // 'deviceorientationabsolute', 邪魔
      'error',
      // 'focus', 本質ではない
      'gamepadconnected',
      'gamepaddisconnected',
      'hashchange',
      'languagechange',
      'load',
      // 'message', 多すぎ
      'messageerror',
      'offline',
      'online',
      // 'orientationchange',
      'pagehide',
      'pagereveal',
      'pageshow',
      'pageswap',
      'popstate',
      'rejectionhandled',
      'resize',
      // 'scrollsnapchange',
      // 'scrollsnapchanging',
      'storage',
      'unhandledrejection',
      // 'unload', 権限なし
      // 'vrdisplayactivate',
      // 'vrdisplayconnect',
      // 'vrdisplaydeactivate',
      // 'vrdisplaydisconnect',
      // 'vrdisplaypresentchange',
    ]

    for (const eventName of windowEvents) {
      window.addEventListener(eventName, () => {
        console.log(`[window] ${eventName}`);
      })
    }

    // (1) Document で傍受するイベント
    const documentEvents = [
      // 'afterscriptexecute',
      // 'beforescriptexecute',
      'DOMContentLoaded',
      'fullscreenchange',
      'fullscreenerror',
      'pointerlockchange',
      'pointerlockerror',
      // 'prerenderingchange',
      'readystatechange',
      'scroll',
      'scrollend',
      // 'scrollsnapchange',
      // 'scrollsnapchanging',
      'securitypolicyviolation',
      'selectionchange',
      'visibilitychange',

      'inertia:before',
      'inertia:start',
      'inertia:progress',
      'inertia:success',
      'inertia:flash',
      'inertia:error',
      'inertia:invalid',
      'inertia:exception',
      'inertia:finish',
      'inertia:navigate',
      'inertia:prefetching',
      'inertia:prefetched',
    ]

    for (const eventName of documentEvents) {
      document.addEventListener(eventName, () => {
        console.log(`[document] ${eventName}`);
      })
    }

    // (3) Vue で傍受するイベント(うるさいので消してもよい)
    const vueHooks = [
      'beforeCreate',
      'beforeMount',
      'mounted',
      // 'beforeUpdate', // うるさい
      // 'updated', // うるさい
      'beforeUnmount',
      'unmounted',
      'errorCaptured',
      // 'renderTracked', // うるさい
      // 'renderTriggered', // うるさい
      'activated',
      'deactivated',
      'serverPrefetch',
    ]

    const mixin: Record<string, any> = {}
    for (const hook of vueHooks) {
      mixin[hook] = function () {
        console.log(`[vue] ${hook}`)
      }
    }

    app.mixin(mixin)
  }
}
app.ts
+   import { listenerPlugin } from '@/listenerPlugin'
    
    createInertiaApp({
        title: (title) => (title ? `${title} - ${appName}` : appName),
        resolve: (name) =>
            resolvePageComponent(
                `./pages/${name}.vue`,
                import.meta.glob<DefineComponent>('./pages/**/*.vue'),
            ),
        setup({ el, App, props, plugin }) {
            createApp({ render: () => h(App, props) })
+               .use(listenerPlugin)
                .use(plugin)
                .mount(el);
        },
        progress: {
            color: '#4B5563',
        },
    });

今回はじめて Vue のプラグインを作ったんですけど、こんな簡単なのですか...。

イベントの発行順序

ではそれぞれの場合を見ていきます。

[vue] のイベントは、全コンポーネント分発火するので、適当に端折ります。
laytou と page だけ、とかには絞れ無さそうだった...。
また状況によって再マウントされたり、使いまわされてたりするから、あくまでも参考程度でお願いします!

▼ ページを初めて表示したとき

Vue のコンポーネントがマウントされてから inertia:navigate が発行されるみたい。

初回通信は HTML の中に Props も含まれるため、inertia:start 系の通信は走らない。

[vue] beforeCreate // コンポーネントの再構築
[vue] beforeMount
[vue] mounted
[document] DOMContentLoaded // 速すぎて出ない時もある...
[document] inertia:navigate // 遷移完了
[document] readystatechange
[window] load
[window] pageshow

▼ ページを遷移したとき

まず inertia:before が発火する。
その後 Vue のコンポーネントがマウントされ、同様に inertia:navigate が発火される。
最後に inertia:finish が発火する。

Inertia 本体の通信は routeruseForm にある OnSuccess OnFinish が発火するので、ページ遷移したあとも処理が残ってるんですねぇ。

[document] inertia:before
[document] inertia:start
[vue] beforeUnmount // コンポーネントの再構築
[vue] beforeCreate
[vue] beforeMount
[vue] unmounted
[vue] mounted
[document] inertia:navigate // 遷移完了
[document] inertia:success
[document] inertia:finish

▼ ブラウザの「戻る」を押したとき

window.history.back() と同義。

inertia:start が発火しないことから分かる通り、Inertia ではページを戻ってもデータ通信は行いません。History に記録されているデータを読み出します。
(一応 MAX 18MB だったっけな)

もしデータの再読み込みが必要な場合は、reload() なりの処理を埋め込み必要があります。

[window] popstate
[vue] beforeUnmount // コンポーネントの再構築
[vue] beforeCreate
[vue] beforeMount
[vue] unmounted
[vue] mounted
[document] inertia:navigate

ちなみに「進む」も同じ挙動です。

▼ フォームを送信したとき(成功)

inertia 系のイベントのみ発火した。
Inertia では Props データのみ更新されるため、コンポーネントの再構築は行われない。
その代わり裏で [vue] updated が呼ばれている。

ページ遷移もしていないので inertia:navigate は発火しない。

[document] inertia:before
[document] inertia:start
[document] inertia:success
[document] inertia:finish

▼ フォームを送信したとき(エラー)

成功時の successerror に変わっただけ。
もしエラーハンドリングが行いたい場合は inertia:error に差し込むと良い。

[document] inertia:before
[document] inertia:start
[document] inertia:error
[document] inertia:finish

router.reload() を実行したとき

router.reload() は、自動的に preserveState: true となります。

そのため、発生するイベントも Props の更新だけになります。

[document] inertia:before
[document] inertia:start
[document] inertia:error
[document] inertia:finish

検証のため、 preserveState: false にしてみると...

router.visit(window.location.href, {
  preserveState:false,
})

再マウントと通信の両方が発火しました。
しかし、同じURLだと inertia:navigate が発火しないのが興味深いです。
replace: true としても変わりませんでした。

[document] inertia:before
[document] inertia:start
[vue] beforeUnmount // コンポーネントの再構築
[vue] beforeCreate
[vue] beforeMount
[vue] unmounted
[vue] mounted
[document] inertia:success
[document] inertia:finish

おわりに

よく考えられてますねぇ...
何か特殊なことをしたい場合は、ぜひ参考にしてください!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?