Skip to content

Commit

Permalink
Nuxt
Browse files Browse the repository at this point in the history
  • Loading branch information
ma91n committed Nov 25, 2024
1 parent 6e105db commit fc10ca5
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
title: "Vue.js連載始めます & Nuxtの通信パターンも見てみる"
date: 2024/11/25 00:00:00
postid: a
tag:
- Vue.js
- Nuxt
- フロントエンド
category:
- Programming
thumbnail: /images/20241125a/thumbnail.png
author: 澁川喜規
lede: "フロントエンドフレームワークからサーバーにアクセスするパターンという記事を書いたところ、ちょっとバズったので、そういえば調べきれてなかったNuxtの話でも書こうかな、と思って調べてみた記事です。"
---

<img src="/images/20241125a/top.png" alt="" width="800" height="378">


Vue.js連載企画を始めます。今年は勤労感謝の日が土曜日で、勤労に感謝できなくて残念でしたね。

| Date | Name | Title |
|:-|:-|:-|
| 11/25(月) | 渋川よしき | Nuxtの通信パターンも見てみる(この記事) |
| 11/26(火) | 大岩潤矢さん | Vue3・Nuxt3アプリをPWA化する方法 2024年版 |
| 11/27(水) | 村田靖拓さん | 2015年頃のフロントエンジニアだってvoid(0)のワクワクを理解したい |
| 11/28(木) | 永井優斗さん | Vue Fes Japan 2024報告 |
| 11/29(金) | 山本竜玄さん | Deno × Vueを触ってみた(2024年冬) |

# Nuxtの通信パターンも見てみる

[フロントエンドフレームワークからサーバーにアクセスするパターン](/articles/20241111a/)という記事を書いたところ、ちょっとバズったので、そういえば調べきれてなかったNuxtの話でも書こうかな、と思って調べてみた記事です。

# Nuxt3の通信機能

Nuxt3のドキュメントには以下の3つの基本の通信のための機能があります。

| 関数 | 機能 |
|:-|:-|
| `$fetch()` | [ofetch](https://github.com/unjs/ofetch)というブラウザAPIの`fetch()`互換のライブラリの関数 |
| `useAsyncData()` | データ取得のライフサイクル管理やキャッシュ管理をするラッパー |
| `useFetch()` | `$fetch()` + `useAsyncData()` |

現在サポートされているNode.js18以降はみなオプションをつかわずに`fetch()`が使えるのに、なぜ`$fetch()`なんてものを別に用意しているかというと、単なる`fetch()`ではなく、ヘッダーを付与したりプリセットが設定できるような`create()`メソッドが使えたり、拡張されていたりします。何もしなくても、サーバー側で実行すると、ホスト名を省略できるなども設定されています。

https://nuxt.com/docs/guide/recipes/custom-usefetch

`useAsyncData()`そのものには通信機能はなく、中のコールバックで`$fetch()`を呼びます。ただし、通信をキャッシュして2度目以降は呼ばないようにしたり、通信中かどうかのフラグだったり、通信結果のデータだったりを返してくれます。単なる`async`な情報取得関数をこの中で呼ぶだけで表示管理やキャッシュが行えてしまうという優れものです。OpenAPIで作ったクライアントをラップしたりといった使い方もできるでしょう。

```html
<script setup lang="ts">
const { data, status, error, refresh, clear } = await useAsyncData(
'mountains', // キャッシュのキー
() => $fetch('https://api.nuxtjs.dev/mountains')
)
</script>
```

`useFetch()`は、上の2つをまとめて呼ぶヘルパー関数です。コードが短くなります。

```html
<script setup lang="ts">
const { data, status, error, refresh, clear } = await useFetch(
'https://api.nuxtjs.dev/mountains'
)
</script>
```

これ以外に、`useAsyncData()`と、`useLazyFetch()`という派生の関数もあります。

# Nuxtの`useFetch()`/`useAsyncData()`のライフサイクル

`$fetch`単体では、通常の`fetch()`とだいたい同じなので特別なことはないのですが、他の2つは末尾にオブジェクト型でオプションを追加すると、動作が大きく変わります。例えば、`server: false`をつけると、サーバーサイドレンダリング時においても、サーバーからリクエストを行わず、ブラウザの表示後にリクエストされるようになります。

```ts
const { data, status, error, refresh, clear } = await useFetch(
'https://api.nuxtjs.dev/mountains',
{ server: false } // これを追加
)
```

オプションはいろいろあります。

* `server`:サーバー上のデータを取得するかどうか(デフォルトは`true`
* `lazy`:クライアント側のナビゲーションをブロックする代わりに、ルートをロードした後に非同期関数を解決するかどうか(デフォルトは`false`)、`true`にすると、動作が`useLazyFetch()``useLazyAsyncData()`と同じになる
* `immediate``false`に設定すると、リクエストがすぐに起動できなくなります。(デフォルトは`true`
* `dedupe`: すでに保留中のサーバー呼び出しがあった場合の動作
* `'cancel'`: 呼び出し中の古いリクエストをキャンセルする(こちらがデフォルト)
* `'defer'`: 新しい呼び出しの方をキャンセルする
* `watch`: 何かしらのリアクティブを設定すると、それが変更されたときに再リクエストを行う
* `default`: デフォルト値を返す

デフォルトではサーバー側のレンダリング時にリクエストを行います。`server: false`にすると、ブラウザからリクエストが飛ぶようになります。あるいは、そのコードが書かれたコンポーネントが[ClientOnly](https://nuxt.com/docs/api/components/client-only)コンポーネントでラップされた中に置かれていた場合は、それはすべてクライアント側でおこなれわれています。

`default`のデフォルト値設定と、`lazy: true`もしくは、Lazyがつく方のメソッドを使う、`immediate: false`を組み合わせると、まず初期値を返し、後から結果を返すことができます。これが`server: false`であれば前のエントリーのStale-While-Revalidateのようになりますし、`server: true`であればサーバーコンポーネントのような動きになります。

初期値は`default`を設定すれば最初から返せますし、`useFetch()``useAsyncData()`が返す`status`を見ることで、未ロードかどうかをハンドリングできます。

これらのオプションをうまく使い分けると多くのパターンが実現できることがわかります。

## 少し問題

ただ、色々試していて、サーバーアクセスする2つのコンポーネントがあった場合に、それぞれの通信でブロックしているような動作をしていました。わかりやすくするためにサーバーAPI側で1秒間のウェイトをかけていたのですが、コンポーネントが2つあると初期表示が2秒になりました。`<Suspense>`とか使ってもカバーできず、直列に待っているようでした。検索してみたら・・・[この](https://github.com/nuxt/nuxt/issues/12391)issueですかね。

Next.jsで同じようなプログラムを作ってみたところ、2つのコンポーネントが並列でリクエストを送っても(URLなどは変えてキャッシュされないようにして)、1秒ですみました。このあたりの並列処理とかはまだNext.jsに一日の長がありますね。

通信するコンポーネントが複数あると遅くなるので、一箇所で行なって`Promise.all()`で並列で待つとかをすれば良いとは思いますが・・・・

あと、experimentalなサーバーコンポーネントを試してみたけども、どうもクライアント側で動いているような感じでした。

# まとめ

Nuxtの通信周りのAPIをさらっとみて実験等をしてみました。これ1つで、さまざまなパターンに対応できる機能で、これはReactとかでも欲しいな、とちょっと思いました。

なお、最初、間違ってページ遷移時のリンクを`<NuxtLink to="遷移先">`ではなく、`<a href="遷移先">`と書いてしまい、ページ遷移後のレンダリングも全てがサーバーサイドレンダリングになってしまい、「サーバーコンポーネントと言っているReactよりもかなり前衛的ですごいじゃん!!!!」と勘違いしてしまい、方針変更してそちらについて書こうと思ってNuxt2との動作比較とかも調べたりしたのですが、`<NuxtLink>`の存在に気づいて書き換えたら、Next.js 12以前と同じような動きになって、原稿を全消ししたりしました。
Binary file added source/images/20241125a/thumbnail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file.
Binary file added source/images/20241125a/top.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit fc10ca5

Please sign in to comment.