「Denoを試す」の続きです。
PreactでSSR
サーバーサイドを動かすためにDenoを使ってみる気になったので、この機にSSR(サーバーサイドレンダリング)を試してみたいと思いました。
普通ならここでReactの出番なのですが、私はPreactで試してみることにしました。
PreactはReactの軽量版として有名で、軽量化のため一部機能は限定されていますが、私はSPA(シングルページアプリケーション)用にすでに使っていて、そんなに機能不足を感じていません。
ReactよりはPreactの方が使い慣れているので、トラブルが少ないと判断したので、Deno×PreactでSSRに挑戦してみました。
Hello World!(HTML版)
Preactの話はいったん忘れ、まずはHTMLを送信する単純なプログラムを作りました。以下がコードです。
Deno.serve(handler);
function handler(_req: Request) {
const html = `
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello, world!</title>
</head>
<body>
<div>Hello, world!</div>
</body>
</html>
`;
return new Response(html, { headers: { "content-type": "text/html; charset=utf-8" } })
}
以下が実行logとcurlのlogです。
> deno --allow-net hello_html1.ts
Listening on http://0.0.0.0:8000/>
> curl localhost:8000
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello, world!</title>
</head>
<body>
<div>Hello, world!</div>
</body>
</html>
もちろん、ブラウザからlocalhost:8000にアクセスすれば Hello, world! が表示されます。
Deno×Preact
いよいよPreactを使ってみましょう。
JSXの使用
まず、JSXを使ってみます。前の例の、bodyの中身を関数コンポーネントにします。
const Hello = () => {
return (
<div>Hello, world!</div>
);
}
しかし、ここでVSCodeのlint機能が以下のエラーログを表示し始めます。
error: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
error: Cannot find name 'React'.
error: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
JSXを使うための設定をしていないので、エラーが出るのは当然ですね。使うための設定をする必要があります。
deno.jsonを作成し、次の様に設定を加えます。
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "preact"
},
"imports": {
"preact": "npm:preact"
}
}
deno.jsonはDenoの設定ファイルです。deno実行時の挙動やVSCodeの拡張機能によるエラーチェックに影響します。
- compilerOptionsで設定でPreactを使うことを宣言する。
- importsの設定で、実際に使うPreactのライブラリの場所が指定される。
これにより、JSXに対するVSCodeのエラーが表示されなくなります。
preact-render-to-stringを使う
送信するHTMLが意図したものになるには、関数コンポーネントを文字列にしてhtml中に埋め込みまなくてはなりません。それには、preact-render-to-stringを使います。以下がコードです。
import { render } from "npm:preact-render-to-string";
Deno.serve(handler);
const Hello = (props: { name: string }) => {
return (
<>
<div>Hello, world!</div>
<div>こんにちは、{props.name}!</div>
</>
);
}
function handler(req: Request) {
const url = new URL(req.url);
const name = url.searchParams.get("name") ?? "(名無しのごんべぇ)";
const html = `
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello, world!</title>
</head>
<body>
${render(<Hello name={name}/>)}
</body>
</html>
`;
return new Response(html, { headers: { "content-type": "text/html; charset=utf-8" } })
}
importによってpreact-render-to-stringをインポートして、render()関数を使うことができるようになります。
このrender()が関数コンポーネントを適切なHTML文字列に変換します。
最初のHello()のままでは、変化がなくて面白くないので、nameによって変化するようにしています。nameはURLのパラメーターから受け取っています。そしてrender()にを渡すことで、サーバーが返すHTMLが変化します。
ブラウザから localhost:8000 にアクセスすれば「(名無しのごんべぇ)」が表示されますし、localhost:8000?name=Purple にアクセスすれば「Purple」が表示されます。
今回の例ではPreactのありがたみを感じませんが、データベースにアクセスしながら大量のデータのテーブルやリストを作るときなどは、JSXで操作できることが役に立ってくれることでしょう。
コメント