hyperappを使って公式に載っているごく簡単なカウンターアプリを作りました。
開発環境の構築
フロントエンドわからないマン(reactの本と公式チュートリアル1周した程度)なのでとにかく楽に導入できる方法を選択しました。
# 作業フォルダの設定
PS> mkdir try-hyperapp
PS> cd try-hyperapp
# package.jsonの作成
PS> yarn init -y
# Hyperappのインストール
# babel-preset-envのインストール(環境に合わせてトランスパイルしてくれる?)
# parcelのインストール(設定ファイルのいらないモジュールバンドラー)
PS> yarn add hyperapp babel-preset-env parcel-bundler
最終的なpackage.json
は以下のようになりました。
{
"name": "try-hyperapp",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"babel-preset-env": "^1.6.1",
"hyperapp": "^1.2.5",
"parcel-bundler": "^1.8.1"
},
※↓TypeScriptで書いたのでparcelでバンドルした際に自動で追加されたようです。
"devDependencies": {
"typescript": "^2.8.3"
}
}
TypeScriptで書きたいのでtsconfig.json
を作成します。
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "h",
"sourceMap": true,
"strict": true
}
}
counter.tsx
の作成
まず、ルートディレクトリ直下にsrc
フォルダを作成します。
本当ならactions
フォルダやviews
フォルダに分けたほうが良いのかもしれませんが、今回は簡単にsrc
直下に全てまとめたcounter.tsx
を作成します。
import { h, app, ActionsType, View } from "hyperapp";
interface IState {
count: number;
}
const state: IState = {
count: 0
};
interface IActions {
down: (value: number) => IState;
up: (value: number) => IState;
}
const actions: ActionsType<IState, IActions> = {
down: value => state => ({ count: state.count - value }),
up: value => state => ({ count: state.count + value })
};
const view: View<IState, IActions> = (state, actions) => (
<div class="card center-align">
<div class="card-image">
<h1>{state.count}</h1>
</div>
<div class="card-action">
<button
class="waves-effect waves-light btn-large"
onclick={() => actions.down(1)}
>
sub
</button>
<button
class="waves-effect waves-light btn-large"
onclick={() => actions.up(1)}
>
add
</button>
</div>
</div>
);
app<IState, IActions>(state, actions, view, document.body);
ActionsType
やView
はTypeScriptでもインポートしなくてもコンパイラに怒られませんが、型が定まっているほうがうっかりミスが減ったり補完が効くので導入しました。
actions
のvalue
がany
型になってしまい、若干不安でしたが、
使用する際にはインターフェースの通りnumber
型と推論されていたので安心しました。
Index.htmlの作成
parcelのエントリーポイントになるIndex.html
をルートディレクトリ直下に作成します。
見た目がさみしいのでCDN経由でMaterialize.css
を使います。
<!DOCTYPE html>
<html lang='ja'>
<head>
<meta charset='UTF-8'>
<title>try hyperapp</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/css/materialize.min.css">
</head>
<body>
<script src='./src/counter.tsx'></script>
</body>
</html>
実行
以下のコマンドでdist
フォルダが作られてローカルサーバー立ち上がります。
今回、TypeScriptを使いましたが、インストールされていない場合、自動で行ってくれるようです。
PS> yarn run parcel index.html
感想とわからなかったところ
良かったところ
-
redux
のようなelm
アーキテクチャ?が最初から組み込まれているのがreact
と比べて好印象。 -
action
周りが簡潔なところも好きです。 - TypeScriptと相性がよさそう。
- 補完がバリバリ効いてうっかりミスもなくなる!
- ボイラープレート的なコードが少なく、やりたいことが簡潔に書ける
分からなかったところ
- フォルダ構成のベストプラクティス
- ちょっと大きめのアプリを作成する際は
react
のDucksパターンみたいのがいいのでしょうか?
- ちょっと大きめのアプリを作成する際は
src
.
|_ views
| |_ counter.tsx
| |_ foo.tsx
|_ states
| |_ counter.ts
| |_ foo.ts
|_ actions
|_ counter.ts
|_ foo.ts
-
actions
やstate
の型はinterface
がいいのかtype
がいいのかわからない- 困っているので詳しい方いらっしゃれば教えてください
// interfaceで書く
interface IActions {
down: (value: number) => IState;
up: (value: number) => IState;
}
const actions: ActionsType<IState, IActions> = {
down: value => state => ({ count: state.count - value }),
up: value => state => ({ count: state.count + value })
};
// typeで書く
const actions: ActionsType<IState, IActions> = {
down: value => state => ({ count: state.count - value }),
up: value => state => ({ count: state.count + value })
};
type Actions = typeof actions;
- デバッグ方法が分からない
- vscodeでステップ実行する方法が分からないのでよく調べなければいけない・・・
-
sourcemap:ture
にしたりvscodeにchrome開発者拡張入れたりしたけどダメでした