小さめのWebアプリを用意する必要があったのでサイズの小ささを売りにしているreactライクなアプリをかけるpreactを使って実装をしてみた。
最初はpreactだけでいこうかと思ったけど、バケツリレーはやっぱり面倒なので軽量版reduxのredux-zeroとセットで使ってみた。
Counterのサンプルで自分好みの環境を用意する。
サンプルはこちら
環境
NodeJS: 10.15.x
npm: 6.9
利用ライブラリ
- babel
- webpack
- eslint (base airbnb)
- stylelint
- preact
- redux-zero
- CSS Modules(use CSS-loader)
webpack4の設定
webpack4を利用。今まで使っていたのはwebpack2だったので微妙に設定が変わってる。
modeを設定する必要があるようなのでprocess.env.NODE_ENVみてdevelopmentとproductionを切り替える
const isDebug = process.env.NODE_ENV !== 'production';
module.exports = {
mode: isDebug ? 'development' : 'production',
自前コードと3rd partyのライブラリはファイルを分けたいのでsplitChunksを利用する。
node_modulesのものをvendorとして分離させる。
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /node_modules/,
name: 'vendor',
chunks: 'initial',
enforce: true,
},
},
},
},
moduleの設定
babel-loader, style-loader, postcss-loaderを適用。
babel7からはbabel-preset-envが@babel/preset-envに代わっていた。
polyfill周りの設定が楽になっている模様。
今回はchromeしか使わないのでpolyfillは設定せず。
eslint
reactでよく使うeslint-config-airbnbを持ってくる。
preactの場合、ルールが一部違うのでpreactに影響がありそうなところは無効化する。
rules: {
'no-console': 0,
'react/prop-types': 0,
'react/react-in-jsx-scope': 0,
'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx'] }],
},
functional component
reactの時と同様の記述が可能
import { h } from 'preact'; // eslint-disable-line no-unused-vars
import Styles from './Counter.css';
const Counter = (props) => {
const { count, onClickDecrement, onClickIncrement } = props;
return (
<div className={Styles.box}>
<h1>{count}
<div>
<button type="button" onclick="{onClickDecrement}">
decrement
</button>
<button type="button" onclick="{onClickIncrement}">
increment
</button>
</div>
</div>
);
};
export default Counter;
CSSの読み込み
Style-Loaderを使っているのでCSS moduleとして読み込み可能。
scoped cssが問題なく使える。
container
reduz-zeroのConnectとmapToPropsを使う。
引数でstateとactionsが取れるようになる。
Connectの中がごちゃごちゃしないようにかけばreactと同じ感覚で書ける。
import { h } from 'preact'; // eslint-disable-line no-unused-vars
import { Connect } from 'redux-zero/preact';
import Counter from '../components/Counter';
import actions from '../actions';
const mapToProps = ({ count }) => ({ count });
const Container = () => (
<connect maptoprops="{mapToProps}" actions="{actions}">
{({ count, increment, decrement }) => (
<counter count="{count}" onclickincrement="{increment}" onclickdecrement="{decrement}">
)}
</counter>
);
export default Container;
actionとstore
redux-zeroを使うのでactionが一番変わってくるところ。
storeは変数を定義しておくだけでactionがreduxでいうところのaction+reducerといった感じになりそう。
各methodの引数でstoreのデータもらってreturnしたObjectがstoreのデータにmergeされるようだ。
export default () => ({
decrement: state => ({ count: state.count - 1 }),
increment: state => ({ count: state.count + 1 }),
});
総括
全体通してreactとほぼ同じ感覚で書けそう。
preactのベースは最新のreactより少し遅いが、ちっちゃいアプリ書く分にはこの組み合わせで十分かも。
そこそこ大きくなってもactionをうまく管理できればredux-zeroでもいいような。
課題はactiveなUIComponentがほとんどないことか。かろうじてmaterial uiが生きているようでそれ以外はメンテされていなそう。
自前で作るかcssだけbootstrapとかsemantic-uiもってくればそこそこ見せられるかな。