🦕

Denoのフロントエンド開発の動向【2021年春】

2021/03/29に公開3

はじめに

ここ最近、Denoのフロントエンド向けのエコシステムについて、色々試してみたり、調査などをしていました。

そこで、この記事では、2021年現在のDenoのフロントエンド開発の状況であったり、所感などについてまとめます。

フレームワークについて

Node.jsにおけるフロントエンド開発では、以下のフレームワーク[1]が使われることが多いと思います。

  • React
  • Vue.js
  • Preact

これらのフレームワークは、すでにDenoでも動作します。

またこれらをベースにしたフレームワークがいくつか開発されています。

Aleph.js

Aleph.jsNext.jsに影響を受けたReactベースのフレームワークです。

以下のような機能が提供されています:

  • HMRをサポートするdevサーバを提供
  • ファイルシステムベースのルーティング
  • SSR/SSGをサポート
  • CSS Modules

開発の活発さや完成度などの点などから、個人的には現時点で最も有望なフレームワークなのではないかと考えています。

とはいえ、現時点ではまだメジャーリリースされておらず、本格的に採用するのは流石にリスキーだと思います。

vno

vnoはDenoでVue.jsアプリケーションを開発するためのフレームワークです。

以下のような特徴があります:

  • Vue.jsのSFC(単一ファイルコンポーネント)のビルド及びバンドルをサポート
  • Vue2及び3の両方をサポート
  • シンプルなdevサーバを提供

使ってみた印象としては、devサーバがファイルの変更監視やHMRなどに対応しておらず、現時点だと本格的な開発に使用するのはまだ厳しい印象です (vno@v1.2.2時点)

将来的にそれらをサポートすることは検討されているので、今後に期待。

dext.ts

dext.tsPreactをベースにしたフレームワークです。

Denoのコアコントリビュータである lucacasonato氏によって開発されています。

こちらもAleph.jsと同様に、ファイルシステムベースのルーティングやSSGなどの機能を提供しています。

ちなみに、deno lintの公式サイトはこのdext.tsとTailwind CSSをベースに構築されています。

バンドラ

現時点で、Denoで動作するバンドラはいくつか存在するものの、デファクト的な立ち位置のものはまだ存在しない印象です:

deno bundle

deno bundleは、Denoに標準搭載されているバンドラです。

さすがにesbuildには及ばないものの、Rustで書かれたswc_bundlerを使用しているため、比較的高速に動作します。

ただし、プラグインシステムなどはないため、現時点で本格的なフロントエンド開発に使用するのは難しいのではないかと考えています。

deno-rollup

deno-rollupは、名前の通りRollupをベースにしたバンドラです。

後述するluathなどでも採用されており、開発も活発に行われていることなどから、個人的に注目しています。

denopack

denopackは、deno-rollupと同様、Rollupをベースにしたバンドラです。

先程紹介したdext.tsで採用されています。

Bundler

Bundlerは、名前の通りDenoで実装されたバンドラです。

他のバンドラにない大きな特徴として、fetchService WorkerWebWorkerなどもサポートされています。

No-bundlerビルドツール

Node.jsでは、ここ数年でブラウザでのES Modulesの対応が進んだこともあり、Snowpack/Vite/WMRといった、新興のビルドツール[2]が登場しました。

これらのツールの共通点として、以下の点が挙げられると思います:

  • ES Modulesを前提としている
  • オンデマンドでのモジュールの変換 及び HMRを提供するdevサーバを提供
    • 必要になるまでモジュールをビルドしないことにより、プロジェクトサイズが増大しても、devサーバの起動が低速化しにくくなるなどの利点があります。
  • Webpack/Rollupなどを利用したプロダクションビルドをサポート

DenoにおけるNo-bundlerビルドツールとしては、以下のものが存在します:

luath

luathはViteやWMRに影響を受けたビルドツールです。

現時点では、以下のような機能が提供されています:

  • HMRやオンデマンドでのモジュール変換を提供するdevサーバ
  • Rollupベースのプラグインシステム
  • PostCSSとCSS Modulesをサポート
  • JSONや画像ファイルなどのimportをサポート

試してみた限り、Reactベースのアプリを作るのであれば、現時点でもある程度は動く印象です。

Rollupベースのプラグインシステムを採用しているため、Vue.jsやSvelteを用いた開発もプラグインを用意すれば実現できるようになるかも...

公開されたばかりのツールなどで、実用にはまだ厳しいと思いますが、個人的にはかなり期待しています。

swdev

swdevは、mizchiさんにより開発されているビルドツールです。

まだPoCの段階のようですが、以下のような機能がサポートされているようです:

  • Service Workerを利用したTypeScriptやSvelteファイルの変換
  • Deno.watchFs()ベースのCache Busting
  • Rollupを利用したプロダクションビルド

リンタ/フォーマッタ

Denoにはリンタ(deno lint)とフォーマッタ(deno fmt)が標準で搭載されているので、基本的にはそれらを使用するのがよいと思います。

サードパーティモジュールの管理について

Node.jsだとサードパーティモジュールはpackage.json+npm/yarnで管理されることが多いと思います。

Denoにはこれらの仕組みは存在しないものの、依存関係管理の方法はいくつか考案されています。

現時点でよくみかけるのは、以下の2つの方式ではないかと考えます:

deps.ts

おそらく、現時点で最もよく見かける依存関係の管理方式です。

以下のように、deps.tsというファイルを用意し、そこですべての依存関係を一括して管理します。

deps.ts
export { exists } from "https://deno.land/std@0.90.0/fs/exists.ts";

export {
  dirname,
  fromFileUrl,
  join,
  resolve,
} from "https://deno.land/std@0.90.0/path/mod.ts";

export { BufReader } from "https://deno.land/std@0.90.0/io/bufio.ts";

// @deno-types="https://unpkg.com/puppeteer@5.5.0/lib/esm/puppeteer/web.d.ts"
export { default as puppeteer } from "https://unpkg.com/puppeteer@5.5.0/lib/esm/puppeteer/web.js";

このようにすることで、依存モジュールのアップグレードが容易になるなどのメリットがあります。(このファイルのexportステートメントを編集するだけで済みます。)

Import maps

Import mapsはWICGで提案されている機能ですが、Denoにもこの機能が搭載されています。

例えば、以下のような内容のJSONファイルを用意します。

{
  "imports": {
    "~/": "./",
    "aleph": "https://deno.land/x/aleph@v0.3.0-alpha.18/mod.ts",
    "aleph/": "https://deno.land/x/aleph@v0.3.0-alpha.18/",
    "antd": "https://esm.sh/antd@4.14.1?external=react@17.0.1",
    "react": "https://esm.sh/react@17.0.1",
    "react-dom": "https://esm.sh/react-dom@17.0.1"
  },
  "scopes": {}
}

こうすることで、アプリケーションでは以下のようにモジュールをimportすることができます。(いわゆるBare import)

import React from "react";

deno runコマンドでスクリプトを実行する際に、--import-mapオプションでこのファイルへのパスを指定することで、denoがBare import文を認識してくれます。

このImport mapsですが、以下の要因から、今後は使用も増えてくるのではないかと想像しています:

また、補足ではありますが、TrexというImport mapsベースのモジュールマネージャも存在します。

タスクランナ

Node.jsだとnpm-scriptsなどが使用されることが多いと思います。

Denoにはpackage.jsonnpmも存在しないので、必要であればタスクランナを導入する必要があります。

現状、以下のツールをよく見かけます:

Velociraptor

Velociraptorは、npm-scriptsの代替として開発されたDenoのタスクランナです。

deno installコマンドでインストールできます。

$ deno install -qA -n vr https://deno.land/x/velociraptor@1.0.0-beta.18/cli.ts

例えば、以下のような内容でプロジェクトのルートディレクトリにscripts.jsonファイルを用意します。

{
  "scripts": {
    "dev": {
      "cmd": "aleph dev",
      "imap": "import_map.json"
    }
  }
}

この状態で以下のコマンドを実行します:

$ vr run

すると、scripts.jsonで定義されたaleph devコマンドが実行されます。

denon

denonnodemonをDenoに移植したツールですが、タスクランナとしても使用することができます。

テストについて

テストフレームワーク

Denoは組み込みのテストランナ(deno test)を提供しています。

そのため、基本的にはdeno testを使用すればよいのではないかと思います。

一応、deno test以外にもRhumwizardなどの選択肢もあり、普段の開発でJestやMochaを使用されている場合は、これらのフレームワークの方が使用しやすいかもしれません。

*-testing-library

試しに、deno_domを使用して、React Testing Libraryを動かそうと試みてみました。

結論としては、importはできるものの、動かすのはまだ厳しそうな感じでした...

test.tsx
import React from "https://esm.sh/react@17.0.1";
import { render, cleanup } from "https://esm.sh/@testing-library/react?external=react@17.0.1,react-dom@17.0.1&no-check";
import { Document } from "https://deno.land/x/deno_dom@v0.1.7-alpha/deno-dom-wasm.ts";

const document = new Document();
document.body = document.createElement("body");
(globalThis as any).document = document;

const Hoge = () => <div>Hoge</div>;

Deno.test("Hoge", () => {
  const hoge = render(<Hoge />); // ここでエラー!
  cleanup();
});

e2eテスト

残念ながら、現時点ではCypressTestCafeなどのツールをDenoで動かすのは厳しいです。

ただし、deno-puppeteerSincoを使えば、Denoでもe2eテストを書けないことはないとは思います。

CDN

DenoにはX-TypeScript-Typesという機能があります。

この機能をサポートするCDNを使用することで、JavaScriptで書かれたモジュールを使用する際にも、型チェックなどを適用することができます。

現時点でこの機能をサポートしたCDNには、以下のものが挙げられます:

ですので、基本的にReactなどのモジュールをimportする際は、これらのCDNを使うとよいのではないかと考えています。

特に、esm.shはAleph.jsの作者であるije氏によってメンテナンスされており、Denoとの相性も非常によく考えられているため、個人的にはとても期待しています。

おわりに

正直なところ、現時点ではDenoをフロントエンド開発に使用するのは少々リスキーというのが個人的な印象です。

色々試してみたものの、特に、Node.jsベースのエコシステム程の安定性は現状ではまだ発揮できないところが辛く感じました。

とはいえ、ここ1年くらいでAleph.jsやluathなどのフレームワークやビルドツールなども徐々に登場しつつあり、この調子で発展していけば、将来的には実用も十分あり得るのではないかと個人的には感じています。

参考

脚注
  1. AngularやSvelteについては、私自身あまり詳しくないので省いています🙇 ↩︎

  2. 正式になんといったらよいかわからないため、仮に「No bundlerビルドツール」と呼んでいます。 ↩︎

Discussion

standard softwarestandard software

濃ゆい情報ありがとうございます。とてもためになります。

バンドラもタスクランナーも、標準で用意しよう、というのがDenoの世界感だったように思いますが、やはり標準じゃ満足できなくてカオスになっていって、Node時代とあまり変わらないような気がしています。

DenoがNode上位互換なら問題なかった気がしますが残念。
まだまだ様子見な感じしました。

shunya_watanabeshunya_watanabe

記事参考になりました、ありがとうございました。

pre-commit hooksに関して質問があります。
npm/yarnを使うときはhuskyを使ったりすると思うのですが、denoでは皆さんどうされたりするのでしょうか?

uki00auki00a

shunya_watanabeさん

コメントありがとうございます!

pre-commit hooksに関して質問があります。
npm/yarnを使うときはhuskyを使ったりすると思うのですが、denoでは皆さんどうされたりするのでしょうか?

今のところ、husky相当のモジュールはDenoには存在しないようです。
そのため、GitHub Actionsでdeno fmt --checkdeno lintコマンドを実行し、一定のフォーマットを保証しているリポジトリが多いように感じています🙂