Fresh 入門
はじめに
Freshに関する概要などについては、
Freshを参照ください🙇♂️
プロジェクトの初期化
init.ts
を実行すると、プロジェクトが作成されます
shell$ deno run -A -r https://fresh.deno.dev <ディレクトリ名>
deno.jsonや
routes/
ディレクトリなど、開発に必要なファイルが自動生成されます。
devサーバの起動
開発をする際は、devサーバを起動する必要があります。以下のコマンドによって起動することが可能です。(
Freshでは開発に必要な各種コマンドは
deno task経由で提供されます)
ルーティング
routesディレクトリ
Routeを配置するディレクトリです。
routes
ディレクトリ内の構造を元に、最終的なパスが決定されます。
Dynamic routes
routes/users/[id].tsx
のような形式でファイルを用意すると定義できます。
Route Groups
Route Groupsを定義する際は、 routes/
配下に (some-group)
というような形式のディレクトリを用意します。
例)
textroutes
├── (_islands)
│ └── Counter.tsx
├── (dashboard) # Route Groupの定義
│ ├── _layout.tsx # Route GroupごとにLayoutを適用できます
│ ├── _middleware.ts # Route GroupごとにMiddlewareを適用できます
│ └── account.tsx # /accountにマッピングされます (`(dashboard)`の部分はパスから取り除かれます)
├── _app.tsx
├── _layout.tsx
└── index.tsx
例外として、
(_some-group)
というように
_
から始まるグループについては
Freshによってファイルシステムルーティングの対象から除外されます。
この性質を利用することで、例えば、 (_components)
のようなディレクトリを用意し、そこで特定のページに関連するコンポーネントの一覧をまとめて管理することもできます。
さらに例外として、
(_islands)
というディレクトリについては
Freshによって特別扱いされます。具体的には、
このディレクトリ配下の各モジュールをFreshはIslandコンポーネントとして取り扱います。
Route
Routeとはページまたはハンドラのいずれかまたはその両方を定義したモジュールのことです。
ページ
RouteからPreactコンポーネントを
default export
すると、対応するパスにアクセスした際にそのコンポーネントが
SSRされます。
ハンドラ
後述するカスタムハンドラを定義することで、ページのレンダリング時の挙動のカスタマイズなどが可能です。
ある
Routeでカスタムハンドラを定義していない場合、同一
Routeで
default export
しているページコンポーネントを
SSRするハンドラがデフォルトで実行されます。
カスタムハンドラ
APIエンドポイントの作成やなど様々な用途で活用できます。
カスタムハンドラを定義したいときは、Routeから下記いずれかの形式で handler
を export
する必要があります。
(request: Request, ctx: HandlerContext) => Response | Promise<Response>
{ [httpMethod: string]: (request: Request, ctx: HandlerContext) => Response | Promise<Response> }
カスタムハンドラで ctx.render()
を呼ぶと、同一Routeで定義されているページコンポーネントがレンダリングされます。
また、 ctx.render()
に渡した引数は、対応するページコンポーネントの props.data
経由でアクセスできます。
typescript // routes/users/[id].tsx
import type { Handlers, PageProps } from "$fresh/server.ts";
export const handler: Handlers<Data> = {
async GET(req, ctx) {
const user = await findUserByID(ctx.params.id);
if (user == null) {
return ctx.renderNotFound();
}
const resp = await ctx.render(user);
return resp;
},
};
export default async function User(props: PageProps<User>) {
return <UserDetail user={props.data} />;
}
Async route component
Freshが提供する独自形式のコンポーネントです。
Request
オブジェクトと RouteContext
を受け取り、Vnodeを返却する関数です。
typescript// routes/users/[id].tsx
import { defineRoute } from "$fresh/server.ts";
export default defineRoute(async (req, ctx) => {
const user = await findUserByID(ctx.params.id);
if (user == null) {
return ctx.renderNotFound();
}
return <UserDetail user={user} />;
});
Islands
概要
Hydrationが必要な場合は、Islandコンポーネントを実装する必要があります。
Freshでは
islands
ディレクトリまたは
routes/**/(_islands)
ディレクトリに配置したコンポーネントは、Islandとして扱われます。
命名形式
Island
コンポーネントのファイル名はパスカルケースまたはケバブケース形式で命名する必要があります。
ミドルウェア
ミドルウェアを定義することで、ハンドラの実行前後に任意の処理を挟めます。
routes
ディレクトリ配下に _middleware.ts
を配置し、 handler
関数を export
することで定義できます。
handler
は引数として Request
と MiddlewareHandlerContext
を受け取り、 MiddlewareHandlerContext
の next
を呼ぶことで、後続のハンドラが実行されます。
エラーページ
404エラー
routes/_404.tsx
でコンポーネントをdefault exportすることで、404エラー用のページをカスタマイズできます (コンポーネントには UnknownPageProps
が渡されます)
404ページを明示的にレンダリングしたいときは、 HandlerContext#renderNotFound
を利用できます。
500エラー
routes/_500.tsx
でコンポーネントをdefault exportすることで、500エラー用のページをカスタマイズできます (コンポーネントには ErrorPageProps
が渡されます)
Custom App
routes/_app.tsx
に配置します。
Custom Appコンポーネントに渡される
props
については
AppProps型を参照
Layout
Custom App( routes/_app.tsx
)はアプリケーションにつき一つしか配置できない制限があります。
それに対して、Layoutは routes
内の任意のディレクトリに _layout.tsx
を配置することで定義できます。
typescript// routes/admin/_layout.tsx
import type { LayoutProps } from "$fresh/server.ts";
export default function AdminLayout({ params, Component }: LayoutProps) {
return (
<section>
<h2>Admin</h2>
<div>
<Component />
</div>
</section>
);
}
Freshで提供されるコンポーネント
<Head>
- <head>
タグの内容を組み立てる際に使用できます。 (将来的に非推奨化される予定です)
静的ファイル
static
ディレクトリに配置したファイルは静的に配信されます
Freshが提供する
asset
関数を利用することで、
static
ディレクトリ内のファイルへの絶対パスをビルドID付きで取得することもできます。
また、
Freshは
<img>
や
<source>
の
src
や
srcset
属性のパスに対して、自動でビルドIDを付与してくれます。(この挙動を無効化したいときは、
data-fresh-disable-lock
を指定する必要があります)
事前ビルド (AOTコンパイル)
Freshでは
deno task build
コマンドによって、Islandコンポーネントなどを事前にビルドすることができます。
このコマンドを実行すると、
esbuildによるバンドル結果が
_fresh
ディレクトリに出力されます。
Freshはもし
_fresh
ディレクトリが存在すれば、そこに格納されたバンドルを利用します。
これによりコールドスタート時間の短縮が期待されます。
注意点として、
Freshにおいては事前ビルドはオプトインであり、導入は必須ではありません。
例えば、開発時は事前ビルドは使わずにJITレンダリング方式のみを使用して開発を進め、本番にデプロイするときだけ事前ビルド(AOTコンパイル)を行う、といったことも可能です。
プラグイン
公式では
Twind v0.16向けのプラグインが提供されています。
テスト
アップデート
shell$ deno run -A -r https://fresh.deno.dev/update .