Denoのフロントエンド開発の動向【2022年秋】
半年程前に以下のような記事を書きました。
この半年の間に、Deno本体でnpmパッケージサポートが入るなどいくつか大きな動きがあったため、この記事ではそれらの動向について紹介いたします。
Deno本体でnpmパッケージの読み込みがサポート
Deno v1.25でDeno本体にnpmパッケージのサポートが入りました。
以下のように、npm:<パッケージ名>[@<バージョン>]
形式のURLを指定することで、Denoからnpmパッケージを直接import
することができます。
import chalk from "npm:chalk@5.1.2";
chalk.yellow("foobar");
deno.land/xなどで公開されているパッケージと同様に、npm:
で指定されたnpmパッケージについては、deno run
などのコマンドを実行する際に、npmレジストリから自動的にダウンロードされ、グローバルキャッシュ(DENO_DIR
)に保存されます。
TypeScriptサポートについて
TypeScriptの型定義が同梱されたnpmパッケージについては、deno check
などのコマンドを実行した際に、自動で型チェックを行ってくれます。
もし型定義ファイルが含まれないパッケージに対しても型チェックを適用したい際は、以下のように@deno-types
ディレクティブで型定義を指定する必要があります。
// @deno-types="npm:@types/koa@2.13.5"
import Koa from "npm:koa@2.13.4";
const app = new Koa();
app.use(async ctx => {
ctx.body = "Hello Deno!";
});
app.listen(3000);
Viteを動かす
Deno本体にnpmパッケージのサポートが入ったことにより、DenoでViteが動かせるようになりました。
create-vite-extra
というパッケージではDenoでViteを使用して開発するためのテンプレートが提供されています。
以下のコマンドを実行すると、プロジェクトを生成することができます。
$ deno run --unstable --allow-env --allow-read --allow-write npm:create-vite-extra@latest
以下はdeno-vue
テンプレートを使用した際に生成されるvite.config.mjs
の例です。
import { defineConfig } from 'npm:vite'
import vue from 'npm:@vitejs/plugin-vue'
import 'npm:vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()]
})
現時点では、DenoはまだpeerDependencies
をサポートしていないため、ワークアラウンドとしてvue
パッケージをimportしておく必要があります。
また、deno.json
も生成されます。
{
"tasks": {
"dev": "deno run -A --unstable --node-modules-dir npm:vite",
"build": "deno run -A --unstable --node-modules-dir npm:vite build",
"preview": "deno run -A --unstable --node-modules-dir npm:vite preview",
"serve": "deno run --allow-net --allow-read https://deno.land/std@0.157.0/http/file_server.ts dist/"
}
}
このファイルでは、deno taskコマンドを使ってViteを動かすための様々なタスクが定義されています。
例えば、以下のコマンドを実行すると、Viteを使用してdevサーバが起動されます。
$ deno task dev
--node-modules-dir
について
補足: 先程のdeno.json
におけるdev
タスクの定義では、--node-modules-dir
というオプションが使われていました。
$ deno run -A --unstable --node-modules-dir npm:vite
--node-modules-dir
は、Viteなどのnode_modules
ディレクトリの存在を前提としたパッケージをDenoで動作させるために実装されたオプションです。
このオプションを指定した状態でdeno run
を実行すると、カレントディレクトリにpnpmライクなフォーマットでnode_modules
を作成してくれます。
Vite以外にも、node_modules
がないとうまく動かないパッケージを利用したい際は、このオプションの有効化を試してみるとよいと思います。
今後について
Deno本体にnpmパッケージのサポートが入ったことにより、Viteに限らず、Node.jsの様々な資産をDenoから活かすための余地が生まれました。
そのため、現在、様々なパッケージでDenoサポートに向けた動きが見られます。
例えば、Prismaではv4.5.0で正式にDenoサポートが入りました。
これにより、Deno+Prismaで書いたアプリケーションをDeno Deployなどで動かせるようになりました。
注意点として、Denoにおけるnpmパッケージのサポートは、内部的にはポリフィルをベースに実装されており、場合や用途などによっては、少なからずオーバーヘッドが生じる可能性があるかもしれません。
そのため、SSRなどのパフォーマンスが重視される領域に関しては、依然として、FreshやAleph.jsなどのDenoで実装されたフレームワークなどにも需要が出てくるのではないかと個人的には考えています。
Fresh v1.0
Deno+PreactベースのフレームワークであるFreshのv1.0がリリースされました。
それに伴い、リポジトリがdenoland Organizationに移管され、FreshはDenoの公式フレームワークという位置づけに変わりました (元々、FreshはDeno Land Inc.のメンバーであるLuca Casonato氏が個人で開発していたプロジェクトでした)
v1.0リリース以降も様々な改善が行われており、プラグインシステムの実装やPreact Signalsのサポートなどが実施されています。
ここではこれらの内容について解説していきます。
プラグインシステム
プラグインシステムは、ユーザがプラグインによってFreshの挙動を拡張するために追加された機能です。
例えば、公式ではTwindプラグインが提供されています。
import { start } from "$fresh/server.ts";
import twindPlugin from "$fresh/plugins/twind.ts";
import manifest from "./fresh.gen.ts";
import twindConfig from "./twind.config.ts";
// Twindプラグインを有効化してサーバを起動します。
await start(manifest, { plugins: [twindPlugin(twindConfig)] });
このプラグインを利用することで、tw
を使わずにクラスを記述できるようになります。
プラグイン適用前:
<div class={tw`font-bold`}>foobar</div>
プラグイン適用後:
<div class="font-bold">foobar</div>
現時点では、プラグインは生成されたHTMLに対するスクリプトやスタイルの注入などの用途での使用が想定されています。
将来的には、このプラグインシステムをさらに拡張し、プラグイン経由でRouteやMiddlewareなどを追加できるようにするなど、より幅広い用途で使用できるようにすることが検討されているようです。
Preact Signalsのサポート
2022/09/06にPreact Signalsが公開されました。
Islands間での状態の共有などに活用できることなどから、早速、FreshにもこのPreact Signalsのサポートが入っています。
Fresh公式のinitスクリプトでプロジェクトを生成すると、あらかじめimport_map.json
にPreact Signalsを読み込むための定義が記述されており、以下のようにしてPreact Signalsを利用することができます。
import Button from "../components/Button.tsx";
import { signal } from "@preact/signals";
const count = signal(0);
export default function Counter() {
return (
<div class="flex gap-2">
<p class="flex-grow-1 font-bold text-xl">{count}</p>
<Button onClick={() => count.value++}>+1</Button>
<Button onClick={() => count.value--}>-1</Button>
</div>
);
}
Aleph.js v1 beta
DenoのフレームワークであるAleph.js v1のbetaバージョンが公開されています。
ここでは、直近で行われている変更のうち主要なものをいくつか紹介いたします。
フレームワークサポートの拡充
まず大きな変更点として、すでにサポートされていたReactやVue.jsなどのフレームワークに加えて、新しくSolidとYewのサポートが進められています。
例えば、以下のコマンドを実行することで、Solid向けのプロジェクトを初期化することができます。
$ deno run -A -r https://alephjs.org/init.ts ./aleph --template=solid
これにより、ページコンポーネントなどをSolidで記述することができます。
ただし、Solidについては、ReactやVue.js向けに実装されているuseData
などの機能はまだサポートされていないようなので、注意が必要そうです。
SSGのサポート
v1.0.0-beta.2でSSGが実装されています。
現時点での使用法としては、まずserver.ts
でSSGを有効化する必要があります。(build.ssg
オプションにtrue
を設定)
import { serve } from "aleph/react-server";
import unocss from "./unocss.config.ts";
serve({
ssr: true,
router: {
glob: "./routes/**/*.{ts,tsx}",
},
build: {
ssg: true
},
unocss,
});
次にgetStaticPaths
をexport
したページコンポーネントを用意します。この関数には静的に生成される必要のあるパスの一覧を返却させる必要があります。
ファイル名については、Dynamic routesが適切に機能するよう、動的に置換したい箇所については:<name>.tsx
形式で命名する必要があります。(例: routes/todos/:id.tsx
)
import { Head, useData } from "aleph/react";
import { getTodoById, getTodoIds } from "../lib/todos.ts";
interface Todo {
id: number;
message: string;
completed: boolean;
}
export async function getStaticPaths() {
const ids = await getTodoIds();
return ids.map((x) => `/todos/${x}`);
}
export const data: Data = {
cacheTtl: 0,
get: async (_req, ctx) => {
const { id } = ctx.params;
const todo = getTodoById(Number(id));
return Response.json(todo);
},
};
export default function TodoDetail() {
const { data: todo } = useData<Todo>();
return (
<main>
<Head>
<title>{todo.message}</title>
</Head>
<div>{todo.message}</div>
</main>
);
}
この状態でビルドを実行すると、output
ディレクトリにHTMLが生成されます。
$ deno task build
Ultra v2 beta
ReactベースのフレームワークであるUltraでv2のリリースに向けて開発が進められています。
v2に向けた大きな変更点として、Ultra v1はwouterやreact-helmetなどの特定のライブラリに強く依存した作りになっていましたが、Ultra v2では様々なエコシステムと柔軟に連携ができるよう改善が進められているようです。
例)
また、Island Architectureのサポートも進められており、今後はFreshとも競合するフレームワークという立ち位置になる可能性がありそうです。
その他には、サーバがHonoをベースに再実装されており、まだどうなるのかはわからないのですが、もしかしたら、Deno Deploy以外の環境(Cloudflare Workersなど)でも動作させられる可能性が出てくるのかもしれません。
RemixでDenoの公式サポートが決定
RemixでのDenoサポートは元々は実験的サポートという位置づけでしたが、v1.5.0で公式サポートが決まりました。
@remix-run/deno
パッケージを利用することで、Remixで開発されたアプリをDenoやDeno Deployなどで動かすことができます。
ただし、現状、依存関係の管理はnpm
によって行う想定であり、開発にはNode.jsが必要であることに注意が必要です。
このあたりの背景については、以下のドキュメントで詳しく解説されています。
Nuxt v3でのDenoサポート
Nuxt 3のサーバエンジンであるNitroでは、Nuxt 3を様々なプラットフォームで動作させるために、presetという抽象化レイヤーを提供しています。
現在、このNitroでDeno presetの実装が進んでいるようです。
これが正式にリリースされれば、Nuxt 3で開発されたアリケーションをDeno Deploy上などで動かせるようになりそうです。
また、Nuxt 3はViteを使っており、Denoのnpmパッケージサポートを利用することにより、ローカル開発もDeno上で行えるようになる可能性もありそうです。
まとめ
この半年間でDenoのnpmパッケージサポートが進むなど、Deno本体で大きな変更が実施されました。Viteに限らず、ESLint/Prettier/stylelint/commitlintなどフロントエンド開発に関わる資産の多くはNode.jsをベースに実装されており、それらを活用できるようになることは大きいことなのではないかと感じています。
また、これらの資産をDenoが提供するパーミッションシステムなどと連携させることで、より安全にそれらのツールを活用できるようになるのではないかと感じています。
その他にも、Fresh v1.0がリリースされ、Denoの公式フレームワークとして扱われるようになったことで、今後さらに開発などが加速していくものと思われます。
今後、Deno Deployが正式にリリースされれば、Freshの需要も少しずつ増していくと思われるため、個人的にはとても注目しています。
Discussion