2023-11-17 Deno Fresh Etude Fresh(1.5.4)
なんもわからんので、まずはドキュメントのままにやる。
Create a project | Fresh docs
ブラウザで見ると、
- VS Codeのプロファイル機能を使い、Deno専用のプロファイルを作る。
- fresh-projectの.vscodeにあるTwind Intellisense (自分で使うって設定したので、入るのは許容する。いらんかったら、前述のproject作成時にdefaultのまま、Nって答えておくといいんでは。
- dev.tsを眺める
- main.tsを眺める
- fresh.gen.ts を眺める
- deno.json を眺める
- routes/ の下を眺める
- islands/ の下を眺める
- ドキュメントに言及がないけど、compomentsの下も眺める
必要そうな知識
- reactとpreactがどれぐらい同じか。react初心者でもpreactを学ぶにはどうするか。
- islandは、ざっとしらべると、その部分だけ更新するしくみというざっくりとした理解だけでやっていけるか
- Twind を使ったCSS指定の使い方について学ぶ。ってか、そもそもCSSわかってないけどな。
TailwindとTwindの関係について、なんか関連してそうかなぁ。とおもって調べると、TwindはTailwind CSSの代替として開発され、サイズが小さいので、ユーザにとっての体験がよくなります。
使い方としては、Twind固有でなく、Tailwind CSSのドキュメントも参考になりそうなのがわかって良かった。
see also
-
FreshでTailwindCSSの最新版(Twind v1)を使う方法
- 2023-11-17 現在でも、やる必要あるのかいな? どうやったら判別できるか書いてないので、素人には追試できない。まあ、やってたらわかるだろ。
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
#!/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);
/// <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"/> 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="..." /> TypeScript: Documentation - Triple-Slash Directives
/// <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
んな感じだしねぇ。
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;
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指定からの類推で、思い当たった。なるほど。
{
"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"
}
}
import { Options } from "$fresh/plugins/twind.ts";
export default {
selfURL: import.meta.url,
} as Options;
プラグイン機構
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
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>
</>
);
}
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>
);
}
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/
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
import { PageProps } from "$fresh/server.ts";
export default function Greet(props: PageProps) {
return <div>Hello {props.params.name}</div>;
}
islands/
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>
);
}
./static/
ロゴとfaviconが置いてある。
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
と同じだが、なにが、どう違うの?
まあ、そのうち、わかるか。ってことにして、先に進もう。
-
deno compileっていつまで使えたっけ? ↩︎
このプロジェクトを自分のマシン(ローカル)で動かしたら、次は新しいルートを追加する。ルートって特に説明してないけど、ほかの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を指定しているが、大きくならないのは、なんかあるんだろうな。後回しやね。
カスタム・ハンドラ
ルートってのは、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 をコピペして実行してみるか。
-
どうやってdebuggerを使うことができるかも学ばないといけない ↩︎
んー、わかんねぇ。
routes/about.tsx をsample通りに変更して、開発者ツールでヘッダーとレスポンスを取得してみたかか、X-Custom-Header
が見当たらない。
なんか、しょーもないミスをしているんだとおもうが。
はい。しょうもないミスでした。handlerって名前が重要だ。と書いていながらtypoしていたというミス。
chromiumの開発者モードで確認していて良かったよ。
コピペしてしていたら、間違えなかったけど、先取りしてミスって教訓を得た。ほかの人は他山の石としてほしい。そして、理由が一瞬でわかったGitは偉大だね。git diffさまさま。
routes/のしたのファイル名を変更したら、関連する fresh.gen.ts も書き換わることを確認した。
実際のプロジェクトでは、APIやデータベース、ディスクからデータを読み取る必要がある。その場合にrouteコンポーネントは非同期処理を行う必要がある。最初のパラメータは、クライアントのrequestオブジェクトを含み、2つ目のパラメータ ctx はrouteパラメータを取得するのに使われます。
例は、GitHub APIからユーザデータを取得する例です。
実際のプロジェクトでは、APIやデータベース、ディスクからデータを読み取る必要がある。その場合にrouteコンポーネントは非同期処理を行う必要がある。最初のパラメータは、クライアントのrequestオブジェクトを含み、2つ目のパラメータ ctx はrouteパラメータを取得するのに使われます。
例は、GitHub APIからユーザデータを取得する例です。
この例で、データを取得して画面に表示する例について学んだ。非同期処理については、イベントループとプロミスチェーンで学ぶJavaScriptの非同期処理を読んだので、理解できたと思う。
フォーム送信
アプリケーションとユーザのデータのやり取りはフォームが重要との話。
絞り込みの例
ここは重要っぽくて、あとで Forms --- Form submisiionsForm | Fresh docs でも取り上げている。
Adding interactivity | Fresh docs
いままでは、クライアント側のJSは含んでなかった。これは回復性(resiliency)[1]とパフォーマンス[2]は良い。インタラクティブ性については制限がでる。[3] JSなしか、サーバでページを全部レンダリングするかを選ぶ。って、その二択じゃない方法の話でしょうに。(SSR: Server Side Rendering)をいうているのでしょうけど。
islands architechture がでてきた。コンポーネントでJSを使っているコンポーネントを島に見立てているんかねえ。
Freshは基本はサーバサイドレンダリングだけど、islands/
というディレクトリの下に、パスカル・ケース[4]またはケバブ・ケース[5]のファイル名のアイランド・コンポーネントを置くと良いらしい。例示してあるファイル名が islands/Counter.tsx
とislands/buy-now-button.tsx
なので、そういうことらしい。
islands/Countdown.tsx を入力して試してみる。
islands/Countdown.tsxに動的な部分を書いて、routes/countdown.tsxから表示させている。
こうやって、動的にカウントダウンするページを生成している。
useSignalとuseEffectを使っている。私にとって足りないのは、この辺の知識だな。
TimeForamtをen-USからja-JPに替えるとちゃんと秒数ってでるやん。
Deploy to production | Fresh docs
https://$PROJECT_NAME.deno.dev にdeployする方法
ここまでで、getting startedは終わる。
つぎは、Conecptsで、Freshの機能を紹介している。
Freshのアーキテクチャについて説明している。どういう方針なのかは書いているが、具体的にこうしているというのは、ピンとこない。
サーバのアーキテクチャで、コンポーネントのルーティングについて解説している。
URLとファイルレイアウトの関係について例示している。ここは設計時に読み返すことになるだろう。
これらのルーティング形式で足りない場合についてカスタマイズする方法についても軽く触れているが実際にやるなら、実験が必要だろう。
Route Group
この概念どういうときに使うのが適切なんだろうか。_layout.tsx
の書き方とかどこに?
#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の関係がわかってないので、意味が分からん。
また戻ってきたときにでもわかったらいいや
App Wrapperって何でしょうかねえ。outer structureをつくるって具体的に何をしてくれますの?
なんか、デフォルトのテンプレートみたいなのかしら?
あと今更だけど、freshで出てくるmiddlewareって何よ(あとで出てくるので、まっとけということのようだ --- 簡単に言うと、routeハンドラの前後にロジックを差し込むものらしい)
Async app wrapper
App wrapperの非同期バージョン
Define helper
手早く非同期のApp Wrapperをつくるのに defineApp というショートカットがあるようだ。
Disabling the app wrapper
たぶん、APIとかは、App Wrapperを無効にしないと困るんでは
HTML Formの扱い方
multipart/form-data
POST request with 例のroutes/subscribe.tsxを見ながら、学んでいくとしよう。
動作としては、email addressを入力させて、/thanks-for-suscribing に遷移する。その時postでemailを送信する。
Interactive islands | Fresh docs
islandsの扱い方2種類
そのまま
素直なのは、これか。
JSXからislandsに渡す方法
islands は children
プロパティ経由でのJSXエレメント・パスをサポートしている。
他のpropsからislandsへの(値の)受け渡し
シリアライズできるものを渡せるので、そのへんの条件を列挙している
islandsのネスト
islandsは入れ子にできる。制限はシリアライズできるpropsを受け取ったときだけの模様だ。
クライアントのみでislandsを描画する
例のように、 IS_BROWSER
を使って判定する