Preactの始め方&Reactとの違い

Reactのサブセットライブラリとして、Preactというものがあります。より軽量なReactという位置付けで、ダウンロードするjsファイルのサイズが肥大しがちなSPAサイトにおいて、パフォーマンスを向上させるための有効な選択肢の一つとして捉えられているようです。

使い方などを勉強してみたので、メモ代わりとしてPreactプロジェクトの始め方、Reactとの違いなどについてまとめておきます。

## Reactとのサイズ差

PreactはReactより軽量ということですが、具体的にどのくらい違うのでしょうか？公式ドキュメントには3kB!!と書かれています。それではReactのライブラリサイズは...？ということで、「[BUNDLE PHOBIA](https://bundlephobia.com/)」というサイトで調べてみると、

- React: 6.4kB（minified）, 2.6kB(minified & gzipped)
- Preact: 9.5kB（minified）, 3.7kB(minified & gzipped)

あら...？Reactのほうが軽くない...？と一瞬思ってしまうのですが、ReactをWebで使うには、react-domも必要です。こいつのサイズを見てみると、

- react-dom: 114.6kB（minified）, 36.2kB(minified & gzipped)

デカい！！Preactではreact-domは必要ないので、その分軽い、ということでしょうか。

## Preactの始め方

Preactの公式ドキュメントに行くと、「[Getting Started](https://preactjs.com/guide/v8/getting-started)」というページがあり、その通りに進めればcreate-react-appに近い感じで簡単にプロジェクトをセットアップできます。

ただ、今回はその方法を使わずに手作業で必要なセットアップを行なっていきます。シンプルにするため、TypeScriptは導入しません。
まずは適当なディレクトリを作成し、npm initを入力。

```bash
mkdir preact-sample
npm init -y
```

なにはともあれ、Preactをインストールします。

```bash
npm i --save preact
```

js, jsxファイルをバンドルするため、Webpack系のライブラリをインストール。

```bash
npm i --save-dev webpack webpack-cli webpack-dev-server
```

続けて、jsxファイルをトランスパイルするためのライブラリをインストール。

```bash
npm i --save-dev @babel/core @babel/preset-env babel-core babel-loader babel-preset-preact
```

.babelrcを作成します。

```:.babelrc
{
  "presets": [
    "@babel/preset-env",
    "preact"
  ]
}
```

次に、webpack.config.jsを作成して設定を記述していきます。エントリポイントはsrc/index.jsx、バンドルファイルの出力先はdist/js/bundle.jsとします。

```js:webpack.config.js
const path = require("path");
const webpack = require("webpack");

module.exports = env => {
  return {
    entry: "./src/index.jsx",
    output: {
      filename: "./js/bundle.js"
    },

    resolve: {
      extensions: [".js", ".jsx"]
    },

    module: {
      rules: [
        {
          test: /\.(js|jsx)$/,
          exclude: /node_modules/,
          use: ["babel-loader"]
        }
      ]
    },

    devServer: {
      contentBase: path.join(__dirname, "./dist"),
      watchContentBase: true,
    }
  }
}
```

次にdistディレクトリを作って、その直下にindex.htmlを作成。

```html:index.html
<html>
<head>
  <title>Preact sample</title>
</head>
<body>
  <div id="app"></div>
  <script src="./js/bundle.js"></script>
</body>
</html>
```

最後にsrc/index.jsxに表示したいコンテンツを書けばOKです。

```jsx:index.jsx
import { h, render } from "preact";

const App = () => {
  return <div>Hello World!</div>
};

render(<App />, document.getElementById("app"));
```

これでwebpackのdevServerを立ち上げれば、Hello World!が表示されるはずです。あとは、普段Reactを書いているのとほぼ同じ感覚で進めていけます。関数コンポーネントやhooksも使えます。

## ReactとPreactの違い

Reactとは何が違うのか、どういった機能が使えないのかが気になるところですが、それらは公式ドキュメントにまとめられています。（[Differences to React](https://preactjs.com/guide/v8/differences-to-react)）

そこまで分量も多くないので、自身で確認してみるのが一番良いと思います。Reactにあって、Preactにない機能の部分だけを抜粋します。

### PropTypesによるバリデーションチェック

こういうのですね（以下のコードはReact公式ドキュメントからの抜粋です）。

```jsx
import PropTypes from 'prop-types';

class Greeting extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}

Greeting.propTypes = {
  name: PropTypes.string
};
```

型チェックをするなら今ならTypeScriptを使うことが多いはずなので、なくてもとくに困らないような気はします。ちなみにPropTypesが必要なら、preact-compatというライブラリを入れることで補完できるようです。

### React.Children

React.Childrenについての解説は[こちら](https://ja.reactjs.org/docs/react-api.html#reactchildren)

props.childrenはよく使いますが、React.Childrenは使ったことがないですね。。。こちらもpreact-compatで補完できるとのことです。

### Synthetic Events

Reactではイベントハンドラに渡されるイベントは全てラップされたものになっています。ラップされたイベントインスタンスは同じAPIインターフェースを持つので、ブラウザ間の差異を意識することなく、イベント処理を行えます。また、Synthetic Eventsインスタンスはプールされ、同じインスタンスが使い回されます。そのために、イベント処理が終了するとインスタンスの各フィールドが全てnullで初期化されるという特徴も持っています。

この仕様を知らずにハマった記憶もありますが、PreactではSynthetic Events機能は提供せず、ブラウザネイティブのイベントインスタンスがイベントハンドラに渡されるようです。これは知っておくべき違いな気がしますね。

## まとめ

パッと調べた感じだと、Preactでよくない？と思えるのですが、他ライブラリとの互換性などの問題もあるのかもしれません。まずは小さいプロジェクトで試してみて、実際に使った場合にどんな問題が起こるのかを把握してから適切に使いたいですね。