Deno Fresh のパーシャル
Fresh のパーシャル
パーシャル
パーシャル(Partials)を使用すると、ページの特定の領域をサーバーからの新しいコンテンツで更新し、ブラウザ全体のリロードを発生させずに処理できます。これにより、ウェブサイトはアプリのように感じられ、必要な部分のみが更新されます。
パーシャルの有効化
パーシャルは、HTML要素にf-client-nav
属性を追加し、ページ内の1つ以上の領域を<Partial name="my-partial">
コンポーネントでラップすることで有効になります。
最も簡単な方法は、routes/_app.tsx
に以下の変更を加えて、すべてのページでパーシャルを有効にすることです。
import { PageProps } from "$fresh/server.ts";
import { Partial } from "$fresh/runtime.ts";
export default function App({ Component }: PageProps) {
return (
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My Fresh app</title>
</head>
<body f-client-nav>
<Partial name="body">
<Component />
</Partial>
</body>
</html>
);
}
f-client-nav
属性を追加することで、<body>
タグの下にあるすべての要素でパーシャルが有効になります。ページの特定の領域をパーシャルとしてマークするには、それをユニークな名前を持つ<Partial>
コンポーネントでラップします。
内部的には、ユーザーが<a>
タグをクリックすると、Freshが新しいページをフェッチし、HTMLレスポンスから該当するコンテンツのみを抽出します。対応するパーシャル領域が見つかると、その中身が更新されます。
注意点
<Partial>
コンポーネントのname
プロパティは、パーシャルの中でユニークである必要があります。Freshはこれを使って、レスポンスのどの部分を現在のページに適用するかを判断します。f-client-nav={false}
を渡すことで、特定の要素以下のすべてのクライアントサイドナビゲーションが無効になります。
パーシャルリクエストの最適化
デフォルトでは、f-client-nav
が設定されると、Freshは次のページ全体をフェッチし、レスポンスの該当部分のみを抽出します。しかし、特定の部分のみをレンダリングすることで、このパターンをさらに最適化できます。これを行うには、リンクにf-partial
属性を追加します。
- <a href="/docs/routes">Routes</a>
+ <a href="/docs/routes" f-partial="/partials/docs/routes">Routes</a>
f-partial
属性がある場合、Freshはhref
属性で定義されたページURLにナビゲートしますが、f-partial
で指定されたURLから更新されたUIをフェッチします。これにより、必要なコンテンツだけを提供する高い効率性を持つルートを作成できます。
典型的なドキュメントページのレイアウトを例に考えます。このようなページでは、メインコンテンツエリアとサイドバーに複数のリンクがあり、ドキュメントのページを切り替えることがよくあります(ここでは緑色で示されています)。
このようなページのコード(スタイリングを除く)は次のようになります:
export default defineRoute(async (req, ctx) => {
const content = await loadContent(ctx.params.id);
return (
<div>
<aside>
<a href="/docs/page1">Page 1</a>
<a href="/docs/page2">Page 2</a>
</aside>
<Partial name="docs-content">
{content}
</Partial>
</div>
);
});
外側のレイアウトやサイドバーをレンダリングせず、コンテンツだけをレンダリングする最適なルートは次のようになります:
import { defineRoute, RouteConfig } from "$fresh/server.ts";
import { Partial } from "$fresh/runtime.ts";
// コンテンツのみをレンダリングするために、
// `_app.tsx`テンプレートや継承されたレイアウトを無効化します
export const config: RouteConfig = {
skipAppWrapper: true,
skipInheritedLayouts: true,
};
export default defineRoute(async (req, ctx) => {
const content = await loadContent(ctx.params.id);
// 新しいコンテンツのみをレンダリング
return (
<Partial name="docs-content">
{content}
</Partial>
);
});
f-partial
属性を追加することで、Freshは新しく追加された/partials/docs/[id].tsx
ルートからコンテンツをフェッチします。
<aside>
- <a href="/docs/page1">Page 1</a>
- <a href="/docs/page2">Page 2</a>
+ <a href="/docs/page1" f-partial="/partials/docs/page1">Page 1</a>
+ <a href="/docs/page2" f-partial="/partials/docs/page2">Page 2</a>
</aside>
この設定により、2つのリンクのいずれかをクリックすると、Freshは新しいページに移動し、最適化されたパーシャルルートでレンダリングされたコンテンツだけをロードします。
なぜか、1度404ページが表示されてから、リロードしたらページが表示される。
注意
f-partial
は現在、<a>
、<button>
、および<form>
要素にスコープされています。将来的に他の要素にも拡張される可能性があります。
複数のパーシャルの同時送信
Freshのパーシャルの便利な点は、1つのレスポンスで複数のパーシャルを返すことができることです。これにより、1回のHTTPレスポンスでページの複数の異なる領域を更新できます。例えば、オンラインショップではこの機能が役立ちます。
export default function AddToCartPartial() {
return (
<>
<Partial name="cart-items" mode="append">
{/* 新しいカートアイテムをここにレンダリング */}
</Partial>
<Partial name="total-price">
<p>Total: {totalPrice} €</p>
</Partial>
</>
);
}
この場合、2つのパーシャルが現在のページに適用されます。
パーシャルの置換モード
デフォルトでは、パーシャル内の全コンテンツが置き換えられますが、新しいコンテンツを追加したり、先頭に挿入したりするシナリオもあります。これは、Partial
コンポーネントにmode
プロパティを追加することで実現できます。
replace
- 既存のパーシャルの内容を置き換える(デフォルト)prepend
- 新しいコンテンツを既存のコンテンツの前に挿入append
- 新しいコンテンツを既存のコンテンツの後に追加
例えば、ログメッセージやリスト形式のデータを表示する場合、append
モードが非常に便利です。
import { Partial } from "$fresh/runtime.ts";
function getNewLogLines() {
return [
"hoge", "fuga", "moge"
]
}
export default function LogView() {
const lines = getNewLogLines();
return (
<Partial name="logs-list" mode="append">
{lines.map((line) => {
return <li key={line}>{line}</li>;
})}
</Partial>
);
}
パーシャルの無効化
特定のリンク、フォーム、ボタンなどでパーシャルリクエストを無効にしたい場合、要素または親要素にf-client-nav={false}
を設定することでそれを回避できます。
<body f-client-nav>
{/* This will cause a partial navigation */}
<a href="/docs/page1">With partials</a>
{/* This WONT cause a partial navigation */}
<a href="/docs/page1" f-client-nav={false}>No partials</a>
{/* This WONT cause a partial navigation on any elements below */}
<div f-client-nav={false}>
<div>
<a href="/docs/page1">No partials</a>
</div>
</div>
</body>;
要素がクリックされると、Freshはその要素にf-client-nav属性が設定されているかどうか、またその値がtrueになっているかを確認します。もしその要素自体にf-client-nav属性がなければ、親要素(祖先要素)にf-client-nav属性が設定されているかどうかを確認します。
f-client-nav属性がtrueの要素が見つかった場合、パーシャルリクエストがトリガーされます。もしf-client-nav属性が設定されていないか、falseに設定されている場合は、パーシャルリクエストは発生しません。
この仕組みにより、特定の要素やその親要素でクライアントサイドナビゲーションを制御し、ページの再読み込みを防ぐことができます。
