Denoを試す2

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で操作できることが役に立ってくれることでしょう。

コメント