Preactで始める軽量コンポーネント指向開発 第1回 Preactの特徴

PreactはReactとほぼ同等の機能を持ち、Reactよりもさらに軽い動作が期待できるUIライブラリです。第1回目はReactとの関係性とPreactの導入について解説します。

発行

著者 杉浦 有右嗣 Jamstackエンジニア
Preactで始める軽量コンポーネント指向開発 シリーズの記事一覧

はじめに

本シリーズでは、PreactというUIライブラリについて解説していきます。

Preactは、軽量版Reactを謳うライブラリであり、現在Google社で働いている@_developit氏によって開発されました。発音としては「プリアクト」が一般的なようです。

この記事を執筆している現在の最新バージョンは10.4.5です。

なお、以降で紹介するサンプルコードは以下のリポジトリにまとめられています。参考にしてください。

Preactではじめる軽量コンポーネント指向開発

PreactとReactの関連性

Preactは、その名前から、また「Fast 3kB alternative to React with the same modern API」というコピーからも推測できるとおり、Reactに関連があります。

Reactは、過去にCodeGridでも取り上げたことがあるUIライブラリですが「その違いは? ReactとPreact、どっちが良いの?」と思った人も多いでしょう。

その違い、Preactが開発された理由を知るためには、まずはReactについておさらいしなければなりません。

Reactというエコシステム

そもそもReact本体(npmから利用できるパッケージとしてのreact)は、汎用的なUI構築のためのライブラリです。

Reactで作ったコンポーネントをどの環境で利用するか、つまりコンポーネントをどのレンダラー(実行環境)で利用するかは、我々に委ねられています。

代表的なレンダラーは次のとおりです。

  • Webブラウザ用のReact DOM(react-dom
  • ネイティブアプリ用のReact Native(react-native

レンダラーはもちろん自作することもできます。

ことWeb開発において「Reactを使う」ということは、暗黙的に「reactreact-domレンダラーをセットで使う」ということになります。

このreactreact-domがやっていることを、これから解説するPreactもやっているのですが、その方法に違いがあります。

ReactとPreactの違い

Reactとの違いについては、公式サイトにも個別のページが用意されていますが、ここではその要点を抜粋して説明します。

最大の違いは、Reactがクリックなどのイベントシステムを独自実装している一方、PreactではDOM本来のAPI(addEventListener()など)を利用しているところです。

その結果、Preactのファイルサイズはとても小さくなっています。

BundlePhobiaというnpmのパッケージがどれくらいの大きさなのかを可視化するサイトがあるのですが、そのサイトでのそれぞれのファイルサイズ(MINIFIED+GZIPED)は次のとおりでした。

パッケージ名 容量 結果
`react@16.13.1` 2.6kB 結果
`react-dom@16.13.1` 35.9kB 結果
`preact@10.4.5` 4kB 結果

もちろんそのパッケージに含まれるすべての機能を利用する場合の数値であり、実際の利用時にはもう少し小さいはずですが、それでもその差は歴然です。

そのほかの違いですが、パッケージとしてのpreactは、reactreact-domが備えるすべてのAPIをそっくりそのまま実装しているわけではありません。ここは少し冒頭のコピーが誤解を招くところかもしれません。

まとめるとPreactは「ブラウザ独自のAPIを利用しつつ、必要最小限のAPIを選びぬいた分だけ軽い、ブラウザ専用のReact」というわけです。

preact/compatという互換レイヤー

PreactはAPIを厳選しているため、Reactをターゲットにして作られた周辺ライブラリは、Preactを利用したプロジェクトでそのまま使えないのも事実です。

また、Reactで作られた既存のアプリケーションをPreactで置き換えたいと思っても、存在しないAPIを使っていた場合は移行の障壁になってしまいます。

Reactとの互換性を重視して、Preactにはpreact/compatという互換レイヤー*が同梱されています。

*注:古いパッケージ

preact-compatというリポジトリとパッケージがありますが、これは古いバージョン向けです。最新版のpreactパッケージでは、preact/compatというネームスペースに同梱されているので、別途インストールの必要はないです。

このpreact/compatというパッケージを、WebpackやRollupなどのバンドラの設定でエイリアス指定し、reactreact-domの代わりに指定します。

webpack.config.jsでの設定例

const config = {
  "resolve": {
    "alias": {
      "react": "preact/compat",
      "react-dom/test-utils": "preact/test-utils",
      "react-dom": "preact/compat",
    },
  }
}

詳細な設定方法については各バンドラのドキュメントを参照してください。併せて公式サイトのガイドも参考にしてください。

このエイリアス設定により、各ライブラリ側が明示的にReactに依存したコードのままで、Preactを利用することができるというわけです。

preact単体よりはファイルサイズがわずかに大きくなりますが、安全に既存のReactアプリケーションの軽量化に取り組むことができます。なお、使用する周辺ライブラリを当初からPreactでの利用を想定しているものに絞るならば、もちろんpreact/compatを使う必要はありません。

このシリーズでもpreact/compatは利用せず、preactのみで扱えるAPIを解説します。

Preactを使ってみよう

それでは実際に、Preactを使ったコードを動かしてみましょう。

まずは最小構成のコードです。

Preactを用いた最小構成のコード例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Minimum</title>
  </head>
  <body>
    <script type="module">
      import { h, render } from "https://unpkg.com/preact?module";

      const app = h(
        "div",
        null,
        ...[h("h1", null, "Hello Preact!"), h("p", null, "Minimum code.")]
      );

      render(app, document.body);
    </script>
  </body>
</html>

これは、次のようなDOMツリーをbody要素に描画するだけのシンプルな例です。

HTML要素を描画する(実行結果)

<div>
  <h1>Hello Preact!</h1>
  <p>Minimum code.</p>
</div>

次のような記述で、CDNに公開されているES Modules版のPreactを利用することで、バンドラ要らずでアプリケーション開発が始められます。

CDNのES Modules版のPreactを使用する

import { h, render } from "https://unpkg.com/preact?module";

次の部分がHTMLを記述している部分なのですが、通常のHTML要素とはかけ離れた書き方をしなければなりません。

HTMLの記述部分(抜粋)

const app = h(
  "div",
  null,
  ...[h("h1", null, "Hello Preact!"), h("p", null, "Minimum code.")]
);

h()はDOMツリーを構成する要素を作り出す関数createElement()のエイリアスなのですが、このままでは少し複雑なHTMLを記述しようとするだけでも苦労しそうです。

より直感的なHTMLの記述でコンポーネントを書けるようにするためには、いくつかのやり方があります。

htmを使う

まずは、おなじくPreactの作者が開発しているhtmというパッケージを紹介します。

先ほどの最小構成コード例と同じことを、htmを使って書き換えるとこうなります。

import文のインポート先をhttps://unpkg.com/htm/preact/standalone.module.jsに変更しています。これでCDN版のhtmが使えるようになります。

htmを用いた最小構成のコード例

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Minimum w/ htm</title>
  </head>
  <body>
    <script type="module">
      import {
        html,
        render,
      } from "https://unpkg.com/htm/preact/standalone.module.js";

      const app = html`
        <div>
          <h1>Hello Preact!</h1>
          <p>Minimum code using htm.</p>
        </div>
      `;

      render(app, document.body);
    </script>
  </body>
</html>

変数appに代入している値を見ると、DOMツリーそのままな直感的な記述ができているのがわかると思います。

appに代入している値(引用)

const app = html`
  <div>
    <h1>Hello Preact!</h1>
    <p>Minimum code using htm.</p>
  </div>
`;

同一ではありませんが、やっていることはlit-htmlというパッケージが実現していることと同一です。lit-htmlに関しては別の記事があるので、ぜひとも参考にしてみてください。

このhtmはPreact専用のパッケージではなく、単体でも利用することもできますが、この例ではpreactが同梱された版を利用しています。

こちらもバンドラが不要でアプリケーションを開発することができるため、おすすめの方法です。

JSXを使う

もう一つの方法として、最後にJSXを使う方法を紹介します。

ReactやVueなど、さまざまなライブラリでも利用される記法なので、馴染みがある人も多いかもしれません。CodeGridでも過去にその記法について紹介したことがあります。

ただしJSXを利用する場合、Babelなどのトランスパイラでの変換が必須となってしまいます。

JSXを使ったコードに書き換えると、このようになります。

JSXを用いた最小構成

import { h, render } from "https://unpkg.com/preact?module";

const App = () => (
  <div>
    <h1>Hello Preact!</h1>
    <p>Minimum code using JSX.</p>
  </div>
);

render(<App />, document.body);

Babelでの変換の都合上、.jsファイルのみを抜粋しています。

Babelを使ってPreactのコードを変換するときに注意すべきはその設定です。

Babelの設定

{
  "plugins": [
    [
      "@babel/plugin-transform-react-jsx",
      {
        "pragma": "h",
        "pragmaFrag": "Fragment",
      }
    ]
  ]
}

React用のプラグインを使って、オプションを指定するかたちで、Preactのコードを変換します。React用のプラグインではpragmaのデフォルト値は、React.createElementpragmaFragのデフォルト値は、React.Fragmentです。これをPreact用に置き換えて、それぞれhFragmentとしています。

Babel自体の使い方や、各種バンドラからBabelを使う方法については、CodeGridの過去記事にも扱ったものがありますので参考にしてみてください。

補足:esbuild

本シリーズのデモのリポジトリでは、Babelではなくesbuildを使ってビルドしています。esbuildを利用する場合でも、Babelの設定と同じようにPreactのための指定は行っています。

こういったバンドラの設定が面倒な場合は、preact-cliを使うことでもセットアップの手間をスキップできます。

よければこちらも参考にしてみてください。

補足:JSX? バンドラ? Babel?

ここまで読んで、「JSX? バンドラ? Babel?」と思った人向けに。詳細はそれぞれの記事を確認していただきたいのですが、ここでも簡潔に説明しておきます。

Webアプリの場合、ブラウザが最終的に実行するのはあくまでJavaScriptです。JSXは、JavaScriptが記述できるものの、それ単体ではJavaScriptとして実行できません。試しにやってみると、HTMLライクな記述の部分でSyntaxエラーになるはずです。

つまり、開発効率のために、我々はJavaScriptを直接使用することを放棄したのです。その代わり、その工程をバンドラ(というよりBabelなどのトランスパイラ)に任せることで、つじつまを合わせています。

この工程では、いわば大掛かりな文字列置換が行われており、もはやなんでもできてしまいます。たとえば日本語で適当に書いたコード文字列を、きちんとしたJavaScriptに変換して利用することも、仕組み上は可能です。

まとめ

今回の記事では、Reactの使い勝手はそのままに、ファイルサイズを劇的に減らすことができるPreactとその導入について解説しました。

ファイルサイズが小さいというのはよいことです。2020年とはいえ、世界の誰しもが高スペック端末と安定したインターネット環境で暮らしているわけではありません。

ファイルサイズが小さいということは、より速くランタイムコードを読み込むことができるだけでなく、理論上はコードパスが短くなるはずで、実行速度にもよい影響があると考えられます。

さて、次回からは、いよいよPreactを使ったコンポーネントの作り方を解説していきます。Preactのシリーズではありますが、Reactのユーザーにも得るところがあるような構成にしていくつもりです。

杉浦 有右嗣
PixelGrid Inc.
Jamstackエンジニア

SIerとしてシステム開発の上流工程を経験した後、大手インターネット企業でモバイルブラウザ向けソーシャルゲーム開発を数多く経験した。2015年にピクセルグリッドへ入社し、フロントエンド・エンジニアとして数々のWebアプリ制作を手掛ける。2018年に大手通信会社に転職し、低遅延配信の技術やプロトコルを使ったプラットフォームの開発と運用に携わっていたが、2020年ピクセルグリッドに再び入社。プライベートでのOSS公開やコントリビュート経験を活かしながら、実務ではクライアントにとって、ちょうどいいエンジニアリングを日々探求している。

この記事についてのご意見・ご感想 この記事をTweet