Reactのサブセットライブラリとして、Preactというものがあります。より軽量なReactという位置付けで、ダウンロードするjsファイルのサイズが肥大しがちなSPAサイトにおいて、パフォーマンスを向上させるための有効な選択肢の一つとして捉えられているようです。
使い方などを勉強してみたので、メモ代わりとしてPreactプロジェクトの始め方、Reactとの違いなどについてまとめておきます。
Reactとのサイズ差
PreactはReactより軽量ということですが、具体的にどのくらい違うのでしょうか?公式ドキュメントには3kB!!と書かれています。それではReactのライブラリサイズは...?ということで、「BUNDLE PHOBIA」というサイトで調べてみると、
- 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」というページがあり、その通りに進めればcreate-react-appに近い感じで簡単にプロジェクトをセットアップできます。
ただ、今回はその方法を使わずに手作業で必要なセットアップを行なっていきます。シンプルにするため、TypeScriptは導入しません。
まずは適当なディレクトリを作成し、npm initを入力。
mkdir preact-sample
npm init -y
なにはともあれ、Preactをインストールします。
npm i --save preact
js, jsxファイルをバンドルするため、Webpack系のライブラリをインストール。
npm i --save-dev webpack webpack-cli webpack-dev-server
続けて、jsxファイルをトランスパイルするためのライブラリをインストール。
npm i --save-dev @babel/core @babel/preset-env babel-core babel-loader babel-preset-preact
.babelrcを作成します。
{
"presets": [
"@babel/preset-env",
"preact"
]
}
次に、webpack.config.jsを作成して設定を記述していきます。エントリポイントはsrc/index.jsx、バンドルファイルの出力先はdist/js/bundle.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>
<head>
<title>Preact sample</title>
</head>
<body>
<div id="app"></div>
<script src="./js/bundle.js"></script>
</body>
</html>
最後にsrc/index.jsxに表示したいコンテンツを書けばOKです。
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)
そこまで分量も多くないので、自身で確認してみるのが一番良いと思います。Reactにあって、Preactにない機能の部分だけを抜粋します。
PropTypesによるバリデーションチェック
こういうのですね(以下のコードはReact公式ドキュメントからの抜粋です)。
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についての解説はこちら
props.childrenはよく使いますが、React.Childrenは使ったことがないですね。。。こちらもpreact-compatで補完できるとのことです。
Synthetic Events
Reactではイベントハンドラに渡されるイベントは全てラップされたものになっています。ラップされたイベントインスタンスは同じAPIインターフェースを持つので、ブラウザ間の差異を意識することなく、イベント処理を行えます。また、Synthetic Eventsインスタンスはプールされ、同じインスタンスが使い回されます。そのために、イベント処理が終了するとインスタンスの各フィールドが全てnullで初期化されるという特徴も持っています。
この仕様を知らずにハマった記憶もありますが、PreactではSynthetic Events機能は提供せず、ブラウザネイティブのイベントインスタンスがイベントハンドラに渡されるようです。これは知っておくべき違いな気がしますね。
まとめ
パッと調べた感じだと、Preactでよくない?と思えるのですが、他ライブラリとの互換性などの問題もあるのかもしれません。まずは小さいプロジェクトで試してみて、実際に使った場合にどんな問題が起こるのかを把握してから適切に使いたいですね。