😽

Astro SPAモード!? View Transition試してみた

2023/08/19に公開

Astro の Veiw Transiton をやっと試したという記事。

はじめに

みなさん、コンテンツ制作では何を使っていますか?

「高速な web サイトの構築」や「結局 jsx(tsx) 書きたいよね」などなどの事情で AstroNext.js を採用するトレンドがあるかと思います。

私自身 Astro の使用感が好きだったのですが、MPA 特有の画面遷移時のちらつきがどうしても好きになれず 最近は Next.js を推していました。
しかし、Astro 2.9 で View Transition を実装したとのことなのでシームレスな遷移ができるようになったみたいです。いざ試す。

View Transition とは

まず、View Transition そのものについて軽く触れておきます。

View の SPA な遷移は DOM を古い状態から新しい状態に変化させる過程で多くの考慮すべきことがあり、なかなか実装が難しいです。この複雑性を隠蔽した API(View Transition API) がChrome v111 からサポートされるようになりました。

Astro の View Transition はこの API を強化する形でページ遷移時のアニメーションを実現します。

「ちょっと Astro は MPA では?」と思われたかもしれませんがちゃんとMPAで実現しています (後述)
*Astroの公式では SPA mode と言うワードが使われています。

View Transition を有効にする

まだ experimental な機能のため諸々の手続きが必要です。

とりあえず環境作る

v2.9 以上を入れてください。

pnpm create astro@latest

有効にする

astro.config.mjs
import { defineConfig } from 'astro/config';

export default defineConfig({
  experimental: {
   viewTransitions: true
  }
});

Head で読み込む

Layout.astro
---
import { ViewTransitions } from "astro:transitions";
// ...
---
<!DOCTYPE html>
<html lang="ja">
<head>
  // ...
  <ViewTransitions />
</head>
</html>

これだけでOK

なんと設定はこれだけです。やばすぎる。

使い方

要素のタイプや位置から Astro が自動で古いページ/新しいページ間の関連付けやアニメーションを行なってくれます。

それだけではなく、transition:* ディレクティブにより柔軟なカスタマイズにも対応可能です。現在、主に3つ用意されています。

  1. transition:name: 古いページと新しいページに対応する DOM 要素の関連付け。基本自動でやってくれるのであまり使わない気もしますが、認識されない時に手動で制御できます。
  2. transition:animate: アニメーションの指定。
  3. transition:persist: 状態の維持。ex. ビデオの再生を別ページに遷移した後も続ける。

試してみた

「記事一覧から記事ページへ」 さらに 「記事ページ間の遷移」 もフェードが自動で付与されます。
ここまでディレクティブで指示はしておらず、全て自動です。恐るべし。

変更がないタイトル要素などもきちんと判断しており、全てをフェードしているわけではないことがわかります。

transition:persist

こちらは v2.10.0 から使用可能です。
動画も画面遷移にとらわれずシームレスに動作可能です。

また、client ディレクティブで定義された同一の Astro Island 間では状態の保持も可能となったようです。

transition:animate

アニメーションの種類を指定できます。
現在、用意されているものは3つあります。

  1. morph(デフォルト): いい感じにしてくれる
  2. slide: 右から Slide in する
  3. fade: 古い要素がフェードアウトした後に新しい要素がフェードインする

試してみた感じ、slidefade は工夫の余地がありそうです。
もちろん、duration や easing など独自のアニメーション定義も可能です。

で、これどう言う仕組み?

View Transision API によってシームレスな遷移が可能になったわけですが、ここで思い出して欲しいのが Astro は MPA だったはずです。MPA ではまだ元となっている JS の View Transition API の遷移は対応されてないんですよね。これは何が起きているのか。

最初にHeadに読み込んだ ViewTransitions.astro に答えがありそうです。
https://github.com/withastro/astro/blob/main/packages/astro/components/ViewTransitions.astro

画面遷移時に html を fetch している

画面遷移が起きた時(ex. <a> タグを押した時)にナビゲーション関数が実行されます。
この関数は遷移先の url に指定されたリンクから html を取得し、View Transition API 内で swp させているんですね。これは元々の View Transition API の強力なところでこれだけでシームレスな遷移が実現されています。

navigate('forward', link.href, { index: ++currentHistoryIndex, scrollY: 0 });
async function navigate(dir: Direction, href: string, state?: State) {
	let finished: Promise<void>;
	const { html, ok } = await getHTML(href);
	// If there is a problem fetching the new page, just do an MPA navigation to it.
	if (!ok) {
		location.href = href;
		return;
	}
	document.documentElement.dataset.astroTransition = dir;
	if (supportsViewTransitions) {
		finished = document.startViewTransition(() => updateDOM(html, state)).finished;
	} else {
		finished = updateDOM(html, state, getFallback());
	}
	try {
		await finished;
	} finally {
		// skip this for the moment as it tends to stop fallback animations
		// document.documentElement.removeAttribute('data-astro-transition');
		await runScripts();
		markScriptsExec();
		onload();
	}
}

つまりちゃんと MPA

あくまでも Client 側でレンダリングはせず「サーバー上でレンダリングした結果を使う」という思想は健在です。
その上で Web 標準の進化に伴い弱点を克服しつつある Astro はやはり強いですね。

ただ、現状あくまでも遷移が起こったタイミングで fetch が走っているので、クライアント-サーバー間が遅いと遷移も遅くなります。

今後 prefetch とかするのかなぁ。また、Web 標準側で MPA 対応されるかもですね。

まとめ

Astro で実装された View Transition API を使ってみました。
個人的な課題感が解決されたので満足な変更です。

この機能により MPA 独自のページ遷移の微妙だった部分を解決してくれそうです。
シームレスな体験を求め Next.js をはじめ SPA フレームワークを使っている方はぜひ Astro の進化を試してみてください。

GitHubで編集を提案

Discussion