1
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
皆さん遊んでいますか!!

結論だけ知りたければ一番下へ。

はじめに

SPA のサイトを組んでいると、ドロップダウンの補完検索だったり、ダイアログ内で動的にデータを取ってきたり、意外と Ajax 通信を行いたい場面が出てきます。

(そういう時に使える Partial reloads は別のお話。部分更新なので、少しだけ用途が異なる。)

例えばユーザー検索機能。
順当にいくと、下記の二種類のルートをメンテナンスしなければなりません。

  • Inertia画面表示用のクエリ構築
  • API用の検索クエリ構築

DDD なりでしっかり組んでおけば Repository 層 にでも逃がせますが、Inertia の特徴の爆速開発とは相性が悪いです。
これをどうにかまとめられないか...。

今回は Welcome ページに以下のデータを付与したサンプルを用意する。

routes/web.php
Route::get('/', function () {
    return Inertia::render('Welcome', [
        'message' => 'データを表示',
        'record' => [
            ['id' => 10, 'name' => '製品A'],
            ['id' => 11, 'name' => '製品B']
        ]
    ]);
})->name('home');

Inertia の仕組み

Inertia のプロトコルは意外と単純。

1回目のページアクセスでは、SPAのサイトHTMLと画面に渡すデータを含めた情報がHTMLで返却されます。
つまりフルリロードがかかります。

image.png

ちなみに初回渡したデータは body > div#app タグ内の data-page にユニコードをエンコードした JSON 形式でそのまま入っています。
これが props としてページコンポーネントに渡る感じですね。

image.png

丸見えが気になる方はこちらの記事を参照してください!

【Laravel Inertia】めちゃ長いdata-pageを非表示にしたい #Vue.js - Qiita

この状態ので次の「Log in」画面へのボタンをクリックすると、先ほどの JSON データのみ取得されます。

image.png

先ほどの Welcome 画面を同じやり方で無理やり閲覧すると、JSON の中にちゃんとデータが入っています。

image.png

この仕組みで、Laravel と連携した SPA が構築されています。

あれ、まとまってない?

よく考えると、Inertia って同じURLへのアクセスの仕組みなのに、フルリロードとAPI取得、使い分けてるな?
これはリクエストを送る際のヘッダーを Inertia が確認して、処理を変えているからです。

X-Inertia: true

フルリロードか JSON 取得を行うかは、このパラメタがキーになっているかで変わる。

フルリロードの際は、このヘッダは未指定です。
JSON取得の際は、X-Inertia: true が付与されます。

X-Inertia-Version: xxxxxxxx

実は X-Inertia: true だけではダメで、この X-Inertia-Version も必要。
これは初期設定ではリソースアセットのハッシュ値が格納されていて、以前と同じリソースを取得しているかを判断している。

バージョン値が異なる場合、 409 Conflict が返ってくるのでセッション中にビルドが走っても安全!
より安全な SPA サイトが構築できる。

ちなみにこれは app/Http/Middleware/HandleInertiaRequests.phpversions() で定義されていて、データの下のほうに表示されている。

image.png

この場合は 865d0c0e6919101770e1358e0634d03d

じゃぁやってみよう (Axios)

では実践あるのみ。
先ほどの Welcome ページを改造してみる。

resources/js/pages/Welcome.vue
<script setup lang="ts">
import { Head, Link, usePage } from '@inertiajs/vue3'
import axios from 'axios'
import { onMounted } from 'vue'

const { version } = usePage()
onMounted(async () => {
    const { data } = await axios.get(route('home'), {
        headers: {
            'X-Inertia': true,
            'X-Inertia-Version': version
        }
    })
    console.log(data)
})
</script>

image.png

ちゃんと値が取れた。
props には、Inertia の値も含まれているので注意。

実践編

実際はこれを Composable なのでラップし、型も当てて使いやすくするとよい。

resources/js/composables/useAxios.ts
import { usePage } from '@inertiajs/vue3'
import axios, { AxiosRequestConfig } from 'axios'

type Method = 'get' | 'post' | 'put' | 'patch' | 'delete'

export const useAxios = () => {
  const ajax = async <T, D = any>(method: Method, url: string, data?: D, config?: AxiosRequestConfig<any>) => {
    const { version } = usePage()

    const fixedConfig: AxiosRequestConfig<any> = {
        ...config,
        headers: {
            ...config?.headers,
            'X-Inertia': true,
            'X-Inertia-Version': version,
        }
    }

    const res = method === 'get'
        ? await axios[method](url, fixedConfig)
        : await axios[method](url, data, fixedConfig)

    const result: T = (res.data as any)?.props

    return result
  }

  const get = async <T>(url: string, config?: AxiosRequestConfig<any>) => {
    return ajax<T>('get', url, undefined, config)
  }

  const post = async <T, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) => {
    return ajax<T, D>('post', url, data, config)
  }

  const put = async <T, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) => {
    return ajax<T, D>('post', url, data, config)
  }
  
  const patch = async <T, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) => {
    return ajax<T, D>('post', url, data, config)
  }

  const del = async <T, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>) => {
    return ajax<T, D>('delete', url, data, config)
  }

  return { get, post, put, patch, del }
}

これを axios の代わりに使う。
すごく簡易実装になっているので、必要なものがあれば増やしてください。

<script setup lang="ts">
import { useAxios } from '@/composables/useAxios'
import { Head, Link, usePage } from '@inertiajs/vue3'
import { onMounted } from 'vue'

const { get } = useAxios()
onMounted(async () => {
    const data = await get<{
        message: string
        record: { id: number, name: string }
    }>(route('home'))

    console.log(data)
})
</script>

おわりに

Ajax ついでに Inertia の仕組みも学べてお得!

マスタ取得系は、このように無理やり取得することが多いですね。
例えばユーザー配列は Props に持たせて Partial Reload するのが奇麗ではあるのですが、
コンポーネントの中にロジックまで閉じ込めたいときは便利です。

みんなも爆速 Inertia 開発!

1
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
1
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?