Open38

2023-11-17 Deno Fresh Etude Fresh(1.5.4)

YABUKI YukiharuYABUKI Yukiharu
  1. VS Codeのプロファイル機能を使い、Deno専用のプロファイルを作る。
  2. fresh-projectの.vscodeにあるTwind Intellisense (自分で使うって設定したので、入るのは許容する。いらんかったら、前述のproject作成時にdefaultのまま、Nって答えておくといいんでは。
  3. dev.tsを眺める
  4. main.tsを眺める
  5. fresh.gen.ts を眺める
  6. deno.json を眺める
  7. routes/ の下を眺める
  8. islands/ の下を眺める
  9. ドキュメントに言及がないけど、compomentsの下も眺める
YABUKI YukiharuYABUKI Yukiharu

必要そうな知識

  • reactとpreactがどれぐらい同じか。react初心者でもpreactを学ぶにはどうするか。
  • islandは、ざっとしらべると、その部分だけ更新するしくみというざっくりとした理解だけでやっていけるか
  • Twind を使ったCSS指定の使い方について学ぶ。ってか、そもそもCSSわかってないけどな。
YABUKI YukiharuYABUKI Yukiharu

TailwindとTwindの関係について、なんか関連してそうかなぁ。とおもって調べると、TwindはTailwind CSSの代替として開発され、サイズが小さいので、ユーザにとっての体験がよくなります。

使い方としては、Twind固有でなく、Tailwind CSSのドキュメントも参考になりそうなのがわかって良かった。

see also

YABUKI YukiharuYABUKI Yukiharu

Projectの初期ファイル一覧

.:
合計 32
drwxr-xr-x 1 yabuki yabuki  238 11月 17 15:42 .
drwxr-xr-x 1 yabuki yabuki   26 11月 17 15:42 ..
-rw-r--r-- 1 yabuki yabuki  146 11月 17 15:42 .gitignore
drwxr-xr-x 1 yabuki yabuki   56 11月 17 15:42 .vscode
-rw-r--r-- 1 yabuki yabuki  363 11月 17 15:42 README.md
drwxr-xr-x 1 yabuki yabuki   20 11月 17 15:42 components
-rw-r--r-- 1 yabuki yabuki 1076 11月 17 15:42 deno.json
-rwxrwxrwx 1 yabuki yabuki  209 11月 17 15:42 dev.ts
-rw-r--r-- 1 yabuki yabuki  219 11月 17 15:42 fresh.config.ts
-rw-r--r-- 1 yabuki yabuki  756 11月 17 16:41 fresh.gen.ts
drwxr-xr-x 1 yabuki yabuki   22 11月 17 15:42 islands
-rw-r--r-- 1 yabuki yabuki  364 11月 17 15:42 main.ts
drwxr-xr-x 1 yabuki yabuki   66 11月 17 15:42 routes
drwxr-xr-x 1 yabuki yabuki   38 11月 17 15:42 static
-rw-r--r-- 1 yabuki yabuki  111 11月 17 15:42 twind.config.ts

./.vscode:
合計 8
drwxr-xr-x 1 yabuki yabuki  56 11月 17 15:42 .
drwxr-xr-x 1 yabuki yabuki 238 11月 17 15:42 ..
-rw-r--r-- 1 yabuki yabuki  91 11月 17 15:42 extensions.json
-rw-r--r-- 1 yabuki yabuki 426 11月 17 15:42 settings.json

./components:
合計 4
drwxr-xr-x 1 yabuki yabuki  20 11月 17 15:42 .
drwxr-xr-x 1 yabuki yabuki 238 11月 17 15:42 ..
-rw-r--r-- 1 yabuki yabuki 353 11月 17 15:42 Button.tsx


./islands:
合計 4
drwxr-xr-x 1 yabuki yabuki  22 11月 17 15:42 .
drwxr-xr-x 1 yabuki yabuki 238 11月 17 15:42 ..
-rw-r--r-- 1 yabuki yabuki 444 11月 17 15:42 Counter.tsx

./routes:
合計 12
drwxr-xr-x 1 yabuki yabuki  66 11月 17 15:42 .
drwxr-xr-x 1 yabuki yabuki 238 11月 17 15:42 ..
-rw-r--r-- 1 yabuki yabuki 773 11月 17 15:42 _404.tsx
-rw-r--r-- 1 yabuki yabuki 369 11月 17 15:42 _app.tsx
drwxr-xr-x 1 yabuki yabuki  14 11月 17 15:42 api
drwxr-xr-x 1 yabuki yabuki  20 11月 17 15:42 greet
-rw-r--r-- 1 yabuki yabuki 767 11月 17 15:42 index.tsx

./routes/api:
合計 4
drwxr-xr-x 1 yabuki yabuki   14 11月 17 15:42 .
drwxr-xr-x 1 yabuki yabuki   66 11月 17 15:42 ..
-rw-r--r-- 1 yabuki yabuki 1174 11月 17 15:42 joke.ts

./routes/greet:
合計 4
drwxr-xr-x 1 yabuki yabuki  20 11月 17 15:42 .
drwxr-xr-x 1 yabuki yabuki  66 11月 17 15:42 ..
-rw-r--r-- 1 yabuki yabuki 146 11月 17 15:42 [name].tsx

./static:
合計 28
drwxr-xr-x 1 yabuki yabuki    38 11月 17 15:42 .
drwxr-xr-x 1 yabuki yabuki   238 11月 17 15:42 ..
-rw-r--r-- 1 yabuki yabuki 22382 11月 17 15:42 favicon.ico
-rw-r--r-- 1 yabuki yabuki  1028 11月 17 15:42 logo.svg
YABUKI YukiharuYABUKI Yukiharu
dev.ts
#!/usr/bin/env -S deno run -A --watch=static/,routes/

import dev from "$fresh/dev.ts";
import config from "./fresh.config.ts";

import "$std/dotenv/load.ts";

await dev(import.meta.url, "./main.ts", config);
main.ts
/// <reference no-default-lib="true" />
/// <reference lib="dom" />
/// <reference lib="dom.iterable" />
/// <reference lib="dom.asynciterable" />
/// <reference lib="deno.ns" />

import "$std/dotenv/load.ts";

import { start } from "$fresh/server.ts";
import manifest from "./fresh.gen.ts";
import config from "./fresh.config.ts";

await start(manifest, config);

なるほど、 /// は、TypeScript: Documentation - Triple-Slash Directives か。

/// <reference no-default-lib="true"/>
This directive marks a file as a default library. You will see this comment at the top of lib.d.ts and its different variants.

This directive instructs the compiler to not include the default library (i.e. lib.d.ts) in the compilation. The impact here is similar to passing noLib on the command line.

Also note that when passing skipDefaultLibCheck, the compiler will only skip checking files with /// <reference no-default-lib="true"/>.

/// <reference lib="..." />
This directive allows a file to explicitly include an existing built-in lib file.

Built-in lib files are referenced in the same fashion as the lib compiler option in tsconfig.json (e.g. use lib="es2015" and not lib="lib.es2015.d.ts", etc.).

For declaration file authors who rely on built-in types, e.g. DOM APIs or built-in JS run-time constructors like Symbol or Iterable, triple-slash-reference lib directives are recommended. Previously these .d.ts files had to add forward/duplicate declarations of such types.

For example, adding /// <reference lib="es2017.string" /> to one of the files in a compilation is equivalent to compiling with --lib es2017.string.

う-ん、どこの型情報をみてるんかなぁ。手元のDebian GNU/Linux Bookwormだと、/usr/share/code/resources/app/extensions/node_modules/typescript/lib/ かしらねぇ。

$ pushd /usr/share/code/resources/app/extensions/node_modules/typescript/lib/
[snip!]
$ ls -la lib.es2015.*
-rw-r--r-- 1 root root  5229 10月 27 10:32 lib.es2015.collection.d.ts
-rw-r--r-- 1 root root 21204 10月 27 10:32 lib.es2015.core.d.ts
-rw-r--r-- 1 root root  1241 10月 27 10:32 lib.es2015.d.ts
-rw-r--r-- 1 root root  2552 10月 27 10:32 lib.es2015.generator.d.ts
-rw-r--r-- 1 root root 14897 10月 27 10:32 lib.es2015.iterable.d.ts
-rw-r--r-- 1 root root  3199 10月 27 10:32 lib.es2015.promise.d.ts
-rw-r--r-- 1 root root  5251 10月 27 10:32 lib.es2015.proxy.d.ts
-rw-r--r-- 1 root root  6493 10月 27 10:32 lib.es2015.reflect.d.ts
-rw-r--r-- 1 root root  1648 10月 27 10:32 lib.es2015.symbol.d.ts
-rw-r--r-- 1 root root 10623 10月 27 10:32 lib.es2015.symbol.wellknown.d.ts

んな感じだしねぇ。

YABUKI YukiharuYABUKI Yukiharu
fresh.gen.ts
import * as $0 from "./routes/_404.tsx";
import * as $1 from "./routes/_app.tsx";
import * as $2 from "./routes/api/joke.ts";
import * as $3 from "./routes/greet/[name].tsx";
import * as $4 from "./routes/index.tsx";
import * as $$0 from "./islands/Counter.tsx";

const manifest = {
  routes: {
    "./routes/_404.tsx": $0,
    "./routes/_app.tsx": $1,
    "./routes/api/joke.ts": $2,
    "./routes/greet/[name].tsx": $3,
    "./routes/index.tsx": $4,
  },
  islands: {
    "./islands/Counter.tsx": $$0,
  },
  baseUrl: import.meta.url,
};

export default manifest;
YABUKI YukiharuYABUKI Yukiharu
fresh.config.ts
import { defineConfig } from "$fresh/server.ts";
import twindPlugin from "$fresh/plugins/twind.ts";
import twindConfig from "./twind.config.ts";

export default defineConfig({
  plugins: [twindPlugin(twindConfig)],
});

こいつを

export default defineConfig({
  hostname: "0.0.0.0",
  plugins: [twindPlugin(twindConfig)],
});

とか、0.0.0.0を引ける名前にするといいのはわかったが、これ、廃止予定なんでどうするのがいいの?コンテナとかの開発で設定がいると思うんだけど。

あー。わかった。

HOSTNAME=0.0.0.0 deno task start

こうするのがよさそうなのか。PORT指定からの類推で、思い当たった。なるほど。

YABUKI YukiharuYABUKI Yukiharu
deno.json
{
  "lock": false,
  "tasks": {
    "check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx",
    "start": "deno run -A --watch=static/,routes/ dev.ts",
    "build": "deno run -A dev.ts build",
    "preview": "deno run -A main.ts",
    "update": "deno run -A -r https://fresh.deno.dev/update ."
  },
  "lint": {
    "rules": {
      "tags": [
        "fresh",
        "recommended"
      ]
    }
  },
  "exclude": [
    "**/_fresh/*"
  ],
  "imports": {
    "$fresh/": "https://deno.land/x/fresh@1.5.4/",
    "preact": "https://esm.sh/preact@10.18.1",
    "preact/": "https://esm.sh/preact@10.18.1/",
    "preact-render-to-string": "https://esm.sh/*preact-render-to-string@6.2.2",
    "@preact/signals": "https://esm.sh/*@preact/signals@1.2.1",
    "@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.5.0",
    "twind": "https://esm.sh/twind@0.16.19",
    "twind/": "https://esm.sh/twind@0.16.19/",
    "$std/": "https://deno.land/std@0.193.0/"
  },
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "preact"
  }
}
YABUKI YukiharuYABUKI Yukiharu
twind.config.ts
import { Options } from "$fresh/plugins/twind.ts";

export default {
  selfURL: import.meta.url,
} as Options;

プラグイン機構

YABUKI YukiharuYABUKI Yukiharu
routes/
drwxr-xr-x 1 yabuki yabuki  14 11月 17 15:42 api
drwxr-xr-x 1 yabuki yabuki  20 11月 17 15:42 greet
-rw-r--r-- 1 yabuki yabuki 773 11月 17 15:42 _404.tsx
-rw-r--r-- 1 yabuki yabuki 369 11月 17 15:42 _app.tsx
-rw-r--r-- 1 yabuki yabuki 767 11月 17 15:42 index.tsx
_404.tsx
import { Head } from "$fresh/runtime.ts";

export default function Error404() {
  return (
    <>
      <Head>
        <title>404 - Page not found</title>
      </Head>
      <div class="px-4 py-8 mx-auto bg-[#86efac]">
        <div class="max-w-screen-md mx-auto flex flex-col items-center justify-center">
          <img
            class="my-6"
            src="/logo.svg"
            width="128"
            height="128"
            alt="the Fresh logo: a sliced lemon dripping with juice"
          />
          <h1 class="text-4xl font-bold">404 - Page not found</h1>
          <p class="my-4">
            The page you were looking for doesn't exist.
          </p>
          <a href="/" class="underline">Go back home</a>
        </div>
      </div>
    </>
  );
}
_app.tsx
import { AppProps } from "$fresh/server.ts";

export default function App({ Component }: AppProps) {
  return (
    <html>
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>fresh-project</title>
      </head>
      <body>
        <Component />
      </body>
    </html>
  );
}
index.tsx
import { useSignal } from "@preact/signals";
import Counter from "../islands/Counter.tsx";

export default function Home() {
  const count = useSignal(3);
  return (
    <div class="px-4 py-8 mx-auto bg-[#86efac]">
      <div class="max-w-screen-md mx-auto flex flex-col items-center justify-center">
        <img
          class="my-6"
          src="/logo.svg"
          width="128"
          height="128"
          alt="the Fresh logo: a sliced lemon dripping with juice"
        />
        <h1 class="text-4xl font-bold">Welcome to Fresh</h1>
        <p class="my-4">
          Try updating this message in the
          <code class="mx-2">./routes/index.tsx</code> file, and refresh.
        </p>
        <Counter count={count} />
      </div>
    </div>
  );
}

routes/api/

joke.ts
import { HandlerContext } from "$fresh/server.ts";

// Jokes courtesy of https://punsandoneliners.com/randomness/programmer-jokes/
const JOKES = [
  "Why do Java developers often wear glasses? They can't C#.",
  "A SQL query walks into a bar, goes up to two tables and says “can I join you?”",
  "Wasn't hard to crack Forrest Gump's password. 1forrest1.",
  "I love pressing the F5 key. It's refreshing.",
  "Called IT support and a chap from Australia came to fix my network connection.  I asked “Do you come from a LAN down under?”",
  "There are 10 types of people in the world. Those who understand binary and those who don't.",
  "Why are assembly programmers often wet? They work below C level.",
  "My favourite computer based band is the Black IPs.",
  "What programme do you use to predict the music tastes of former US presidential candidates? An Al Gore Rhythm.",
  "An SEO expert walked into a bar, pub, inn, tavern, hostelry, public house.",
];

export const handler = (_req: Request, _ctx: HandlerContext): Response => {
  const randomIndex = Math.floor(Math.random() * JOKES.length);
  const body = JOKES[randomIndex];
  return new Response(body);
};

routers/greet

[name].tsx
import { PageProps } from "$fresh/server.ts";

export default function Greet(props: PageProps) {
  return <div>Hello {props.params.name}</div>;
}
YABUKI YukiharuYABUKI Yukiharu

islands/

Counter.tsx
import type { Signal } from "@preact/signals";
import { Button } from "../components/Button.tsx";

interface CounterProps {
  count: Signal<number>;
}

export default function Counter(props: CounterProps) {
  return (
    <div class="flex gap-8 py-6">
      <Button onClick={() => props.count.value -= 1}>-1</Button>
      <p class="text-3xl">{props.count}</p>
      <Button onClick={() => props.count.value += 1}>+1</Button>
    </div>
  );
}
YABUKI YukiharuYABUKI Yukiharu

Running locally | Fresh docs

deno task start を実行すると deno.jsonのtasksの部分で定義してある設定 start を実行する。その時.envから環境変数を読んでくれる。deno.jsonの --watch については、複数指定するには,で区切るといいことがわかる。ドキュメントに書いてある、static/とrouters/を監視対象にしろ。は、すでに設定してあることが確認できる。

そこから派生して、 Command Line Interface | Deno Docs に寄り道すると、deno runだけでなく、deno testやdeno fmt, deno compile[1] コマンドでも--watchが有効なのがわかる。

でも、deno fmtはVS Codeでコード書いていると、自動的に整形してくれるんじゃないの?

よくわからん。runはいいとしても、testは、ターミナルを監視しないといけないのでは?使ってみて便利かどうか判断せんとわからんな。

サーバ側のポート変更方法、ソースを変える方法、コマンドラインで変える方法、環境変数で指定する方法について書いてる。

で、deno runで与えるべきオプションについて説明している。でも、実行対象が dev.tsになってなくてmain.tsなのは何でなの? deno task preview と同じだが、なにが、どう違うの?

まあ、そのうち、わかるか。ってことにして、先に進もう。

脚注
  1. deno compileっていつまで使えたっけ? ↩︎

YABUKI YukiharuYABUKI Yukiharu

Create a route | Fresh docs

このプロジェクトを自分のマシン(ローカル)で動かしたら、次は新しいルートを追加する。ルートって特に説明してないけど、ほかのWeb系のフレームワークで散々つかわれているから省略しているんかな。まあ、経験者向けか。

Webページを表示するのに必要な概念だね。最低限の説明は英文でもしてるか。

URLとアクセスしてきたユーザーやAPIにコンテンツ、APIの場合はデータを返すプログラムを結びつける仕組み。というところか。

ルートの設定は、routes/ の下にあるファイルで行う。モジュール(そこの単位はモジュールなんだ)の命名法保が重要であることの説明がある。この辺は既存のroute設定と同じだね。

  • routes/index.js は / に結び付く
  • routes/about.js は /about に結び付く
  • routes/about/contract.js は /about/contract に結び付く

その下で、routes/におく、about.jsxについて書いている。routes/about.jsx か。jsxに学ぶなら、というリンクがあり、freshはreactではないが、より軽量な仮想DOMライブラリであるPreactを利用しているっていう注意書きがある。 --- どこまで、一緒で、どこが違うのかが知りたいが、まあ後回しやね。

で、routes/about.tsx をコピペして、動かしてみる。まで、やるかね。

routes/にabout.tsxを追加したので、watcherが機能して、リロードをしたのを確認した。pageの表示も確認した。CSSが当たってないので味気ないが。H1を指定しているが、大きくならないのは、なんかあるんだろうな。後回しやね。

YABUKI YukiharuYABUKI Yukiharu

Custom handlers | Fresh docs

カスタム・ハンドラ

ルートってのは、2つのパートから成る。

  1. ハンドラ
  2. ページコンポーネント

で、これまでは、ページコンポーネントについて話をしてきたので、次はハンドラについて話す模様。

ハンドラとは、って表記しているが、これは、同期関数と、非同期関数の話なんかな。Promise<Response>とか書いとるし。そういうのを扱う関数だそうだ。Request => Response or Request => Promise<Response>

Request Objectがあって、Response Objectがあって、ハンドラは全部のHTTPメソッドないしは、メソッドにつき一つのハンドラになるって、どういうことなん? よくわからんが、後になったわかるやろか。

ハンドラは通常カスタムしない。ページコンポーネントを返すようになっとるそうだ。ハンドラはRequest Objectと対になったResponse Objectにアクセスできるっぽい。で、レスポンスオブジェクトは、手動でも作れるってさ。例えばAPIでJSONのレスポンスを返すとかさ。

命名規則として、ハンドラをつくるときは外だしする関数名は、handerにする決まりだそうだ。

ハンドラには2形態あって、plain fuctionか、plain objectだそうです。

例は、HTTPのGETで、カスタムヘッダを追加する。ふむふむ。これ、debugger[1]で resp の中身を覗いて、察しないとダメかな。何が返ってくるとか、どこに記述があるんかな。まあ、後やな。

次は、API routesを使うときに ctx.render()を呼ぶ必要がない例。JSON responseを返す。これは、画面をrenderする必要がないから呼ばなくていいのかな。いまいちわかってない。

例の、routes/about.tsxの更新と実行、routes/api/random-uuid.ts をコピペして実行してみるか。

脚注
  1. どうやってdebuggerを使うことができるかも学ばないといけない ↩︎

YABUKI YukiharuYABUKI Yukiharu

んー、わかんねぇ。

routes/about.tsx をsample通りに変更して、開発者ツールでヘッダーとレスポンスを取得してみたかか、X-Custom-Headerが見当たらない。

なんか、しょーもないミスをしているんだとおもうが。

YABUKI YukiharuYABUKI Yukiharu

はい。しょうもないミスでした。handlerって名前が重要だ。と書いていながらtypoしていたというミス。

chromiumの開発者モードで確認していて良かったよ。

コピペしてしていたら、間違えなかったけど、先取りしてミスって教訓を得た。ほかの人は他山の石としてほしい。そして、理由が一瞬でわかったGitは偉大だね。git diffさまさま。

YABUKI YukiharuYABUKI Yukiharu

routes/のしたのファイル名を変更したら、関連する fresh.gen.ts も書き換わることを確認した。

YABUKI YukiharuYABUKI Yukiharu

Fetching data | Fresh docs

実際のプロジェクトでは、APIやデータベース、ディスクからデータを読み取る必要がある。その場合にrouteコンポーネントは非同期処理を行う必要がある。最初のパラメータは、クライアントのrequestオブジェクトを含み、2つ目のパラメータ ctx はrouteパラメータを取得するのに使われます。

例は、GitHub APIからユーザデータを取得する例です。

YABUKI YukiharuYABUKI Yukiharu

Fetching data | Fresh docs

実際のプロジェクトでは、APIやデータベース、ディスクからデータを読み取る必要がある。その場合にrouteコンポーネントは非同期処理を行う必要がある。最初のパラメータは、クライアントのrequestオブジェクトを含み、2つ目のパラメータ ctx はrouteパラメータを取得するのに使われます。

例は、GitHub APIからユーザデータを取得する例です。

この例で、データを取得して画面に表示する例について学んだ。非同期処理については、イベントループとプロミスチェーンで学ぶJavaScriptの非同期処理を読んだので、理解できたと思う。

YABUKI YukiharuYABUKI Yukiharu

Adding interactivity | Fresh docs

いままでは、クライアント側のJSは含んでなかった。これは回復性(resiliency)[1]とパフォーマンス[2]は良い。インタラクティブ性については制限がでる。[3] JSなしか、サーバでページを全部レンダリングするかを選ぶ。って、その二択じゃない方法の話でしょうに。(SSR: Server Side Rendering)をいうているのでしょうけど。

islands architechture がでてきた。コンポーネントでJSを使っているコンポーネントを島に見立てているんかねえ。

Freshは基本はサーバサイドレンダリングだけど、islands/というディレクトリの下に、パスカル・ケース[4]またはケバブ・ケース[5]のファイル名のアイランド・コンポーネントを置くと良いらしい。例示してあるファイル名が islands/Counter.tsxislands/buy-now-button.tsx なので、そういうことらしい。

islands/Countdown.tsx を入力して試してみる。

islands/Countdown.tsxに動的な部分を書いて、routes/countdown.tsxから表示させている。

こうやって、動的にカウントダウンするページを生成している。

useSignalとuseEffectを使っている。私にとって足りないのは、この辺の知識だな。
TimeForamtをen-USからja-JPに替えるとちゃんと秒数ってでるやん。

脚注
  1. これって、どういう事象を指していうているのか理解できてない ↩︎

  2. ここも、ユーザのブラウザにjsを動かすと遅くなるって、何を心配しているの? ↩︎

  3. わかる。クライアントサイドにJSを注入する方法なんだろうね ↩︎

  4. キャメルケースだけど、最初は大文字 ↩︎

  5. ハイフンで文字をつないだもの ↩︎

YABUKI YukiharuYABUKI Yukiharu

ここまでで、getting startedは終わる。

つぎは、Conecptsで、Freshの機能を紹介している。

YABUKI YukiharuYABUKI Yukiharu

Routing | Fresh docs

サーバのアーキテクチャで、コンポーネントのルーティングについて解説している。

URLとファイルレイアウトの関係について例示している。ここは設計時に読み返すことになるだろう。

これらのルーティング形式で足りない場合についてカスタマイズする方法についても軽く触れているが実際にやるなら、実験が必要だろう。

Route Group

この概念どういうときに使うのが適切なんだろうか。_layout.tsx の書き方とかどこに?

YABUKI YukiharuYABUKI Yukiharu

Routes | Fresh docs

#ArchitectureServer ComponentsRoutingRoutes

この辺はほかでも書いてある内容で、繰り返しになるので重要だと思っていることなんだろうな。

Route GroupsHandler route

うーん、何を解説しているのか、いまいちわからん。 plain textを返すのに、なんで routes/x.ts が必要なんだ?

Component routeComponent route

ここもよくわからん。また後でわかるだろう

Mixed handler and component route

ハンドラの指定は、ページをレンダリングした後に追加されるって、async/awaitしてからなので、当然なのでは

Async route components

ここは重要っぽい。が、routes/html.tsxとroutes/page.tsxの関係がわかってないので、意味が分からん。

また戻ってきたときにでもわかったらいいや

YABUKI YukiharuYABUKI Yukiharu

App wrapper | Fresh docs

App Wrapperって何でしょうかねえ。outer structureをつくるって具体的に何をしてくれますの?
なんか、デフォルトのテンプレートみたいなのかしら?

あと今更だけど、freshで出てくるmiddlewareって何よ(あとで出てくるので、まっとけということのようだ --- 簡単に言うと、routeハンドラの前後にロジックを差し込むものらしい)

Async app wrapper

App wrapperの非同期バージョン

Define helper

手早く非同期のApp Wrapperをつくるのに defineApp というショートカットがあるようだ。

Disabling the app wrapper

たぶん、APIとかは、App Wrapperを無効にしないと困るんでは

YABUKI YukiharuYABUKI Yukiharu

Forms | Fresh docs

HTML Formの扱い方

POST request with multipart/form-data

例のroutes/subscribe.tsxを見ながら、学んでいくとしよう。

動作としては、email addressを入力させて、/thanks-for-suscribing に遷移する。その時postでemailを送信する。

YABUKI YukiharuYABUKI Yukiharu

Interactive islands | Fresh docs

islandsの扱い方2種類

そのまま

素直なのは、これか。

JSXからislandsに渡す方法

islands は children プロパティ経由でのJSXエレメント・パスをサポートしている。

他のpropsからislandsへの(値の)受け渡し

シリアライズできるものを渡せるので、そのへんの条件を列挙している

islandsのネスト

islandsは入れ子にできる。制限はシリアライズできるpropsを受け取ったときだけの模様だ。

クライアントのみでislandsを描画する

例のように、 IS_BROWSERを使って判定する