超軽量Viewライブラリ「HyperApp」の日本語ドキュメント風の何か
Introduction
HyperApp is a JavaScript library for building frontend applications.
HyperAppはWebアプリケーションのフロント(主にView)を担うJavaScript用のライブラリです。このライブラリは3つのコンセプトで成り立っています。
- 外部ライブラリに依存しない、超軽量1KBのライブラリ
- ステートレスコンポーネント
- Elm Architecture に則ったスケーラブル可能な仕組み
今回はHyperAppのWiki及び周辺のドキュメント1を勝手に意訳2してみようと思います😌💡
Getting Started
HyperAppは器用に作られたもので、新規プロダクトに取り入れることはもちろん、既存のWebアプリケーションに組み込むこともできるよ。
一番簡単な方法は何と言ってもCDNだね。
<script src="https://unpkg.com/hyperapp"></script>
バージョンを指定したいかな? そんなときはこう。
<script src="https://unpkg.com/hyperapp@0.7.0"></script>
こんにちは世界
ではさっそくHyperAppを使って「こんにちは世界」を表示させてみよう。index.html
を用意して以下のコードをコピペしてブラウザで見てみよう😄
HyperAppではJSXをつかった記法と、ES6のテンプレートリテラルを使ったHyperxという記法の2つが使えるよ。How wonderful!! それぞれのコード例を用意したから好きな方を使ってね。
JSXで書いた場合
<body>
<script src="https://unpkg.com/hyperapp"></script>
<script src="https://unpkg.com/babel-standalone"></script>
<script type="text/babel">
const { h, app } = hyperapp
/** @jsx h */
app({
model: "こんにちは世界",
view: model => <h1 id="title">{model}</h1>
})
</script>
</body>
Hyperxで書いた場合
<body>
<script src="https://unpkg.com/hyperapp"></script>
<script src="https://wzrd.in/standalone/hyperx"></script>
<script>
const { h, app } = hyperapp
const html = hyperx(h)
app({
model: "こんにちは世界",
view: model => html`<h1>${model}</h1>`
})
</script>
</body>
さて、ブラウザでは何が起きたかな?
ブラウザはHyperxやJSXをCDN経由でダウンロードし、scriptの部分をコンパイルして描画したよ。
この例ではHyperAppとは何かを手軽に知ることができたと思うけれど、これだけだとWebアプリケーション開発の例としては少々物足りないよね。わかるよ。
では、次にWebpack、Browserify、Rollupをつかったビルド環境のセットアップの例を見てみよう。
Build Setup
Webアプリケーションの開発環境を用意するには3つの要素が必要なんだ。
なぜこれらが必要かというと、JSX/Hyperxで書かれたソースをコンパイルするためなんだ💪 コンパイルされると、HyperAppの h関数という仮想DOMを生成するための関数になるのだけれど、それについてはまた後で語るからね。
コンパイル前(JSX/Hyperx)
<h1 id="test">Hi.</h1>
コンパイル後
h("h1", { id: "test" }, "Hi.")
こんな具合だよ。いい具合だね。
さぁ次の章ではJSXとHyperx各々のためのビルド環境のセットアップを学ぶよ。ついておいで!
JSXを使う環境の用意
JSXはXMLと同様データの記法のひとつだよ。知ってたかい? これを使うことでHTML(テンプレート)とJavaScript(処理)を一つのファイルに混在させることができるよ。
JSXを使うには上述の通りコンパイルが必要なんだ。JSXをh関数に変換し、ひとつのjsファイルにバンドルし、配信しなければならないからね。ではさっそくビルド環境の用意をしよう。
Browserifyを使う場合
必要なモジュールのインストール
$ npm install -S hyperapp
$ npm install -D babel-plugin-transform-react-jsx babel-preset-es2015 babelify browserify bundle-collapser uglifyify uglifyjs
.babelrc
の用意
{
"presets": ["es2015"],
"plugins": [
[
"transform-react-jsx",
{
"pragma": "h"
}
]
]
}
ビルドの実行
$(npm bin)/browserify \
-t babelify \
-g uglifyify \
-p bundle-collapser/plugin index.js | uglifyjs > bundle.js
Webpackを使う場合
必要なモジュールのインストール
$ npm install -S hyperapp
$ npm install -D webpack babel-core babel-loader babel-preset-es2015 babel-plugin-transform-react-jsx
.babelrc
の用意
{
"presets": ["es2015"],
"plugins": [
[
"transform-react-jsx",
{
"pragma": "h"
}
]
]
}
webpack.config.js
の用意
module.exports = {
entry: "./index.js",
output: {
filename: "bundle.js",
},
module: {
loaders: [{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}]
}
}
ビルドの実行
$(npm bin)/webpack -p
Rollupを使う場合
必要なモジュールのインストール
$ npm install -S hyperapp
$ npm install -D rollup rollup-plugin-babel rollup-plugin-node-resolve rollup-plugin-uglify babel-preset-es2015-rollup babel-plugin-transform-react-jsx
rollup.config.js
の用意
import babel from "rollup-plugin-babel"
import resolve from "rollup-plugin-node-resolve"
import uglify from "rollup-plugin-uglify"
export default {
plugins: [
babel({
babelrc: false,
presets: ["es2015-rollup"],
plugins: [
["transform-react-jsx", { pragma: "h" }]
]
}),
resolve({
jsnext: true
}),
uglify()
]
}
ビルドの実行
$(npm bin)/rollup -cf iife -i index.js -o bundle.js
Hyperxを使う環境の用意
JSXのときとインストールするファイルが少々変わるだけなので割愛するよ😌
API Reference
ここではHyperAppのモジュールや関数の使い方について、サンプルと併せて紹介するよ。
HyperAppは大きく分けて3つの仕組みを提供しているんだ。
h 関数
… 仮想DOMを生成する関数app 関数
… HyperAppを利用したApplicationを実行する関数Router プラグイン
… ルータ機能群
h
仮想DOMを返す関数だよ。ここで言う仮想DOMとは、ネストされたDOMのツリーをJavaScriptのオブジェクトとして表現しているものだよ🌴
Syntax
h(tag, data, children);
tag {String}
… 「div」など、HTML上でのタグ名data {Object}
… Elementに挿入されるattributeschildren {String | Array}
… 子要素
h('a', {href: '#'}, 'next page');
// return object
// {
// tag: 'a',
// data: {
// href: '#'
// },
// children: 'next page'
// }
app
HyperAppによるWebアプリケーションを起動するよ。これを呼び出すことで全てが始まる。オプションを添えられるよ。
app({
model,
view,
actions,
subscriptions,
plugins,
root
});
model
アプリケーションのStateを管理するオブジェクトだよ。primitiveな値でも、配列でも、Objectでもいいよ。
view
仮想DOMを返す関数だよ。引数にmodel
とactions
をとるよ。
app({
model: true,
actions: {
toggle: model => !model,
},
view: (model, actions) =>
<button onClick={actions.toggle}>
{model.toString()}
</button>
});
actions
Webアプリケーションの振る舞いを定義する関数のコレクションだよ。actions
は一般的にmodel
を更新するために利用されるよ。そのため、返り値が新しいmodel
であることがしばしば。
引数は4つだよ。
model
… 現在のmodeldata
… actionの処理(モデルの更新)に必要な情報actions
… アプリケーションが持つ大元のactionserror
… エラー時に呼ばれるコールバック
app({
model: 0,
actions: {
add: model => model + 1,
sub: model => model - 1,
},
view: (model, actions) =>
<div>
<button onClick={actions.add}>
+
</button>
<h1>{model}</h1>
<button onClick={actions.sub}
disabled={model <= 0}>
-
</button>
</div>
});
subscriptions
DOMContentLoaded
時に一度だけ発火される関数をもつ配列だよ。
app({
model: { x: 0, y: 0 },
actions: {
move: (_, { x, y }) => ({ x, y })
},
subscriptions: [
(_, actions) =>
addEventListener("mousemove",
e => actions.move({
x: e.clientX,
y: e.clientY,
})
)
],
view: model => <h1>{model.x + ", " + model.y}</h1>
});
Router
view
に、PathをKeyにTemplateを登録するとルーティングが行えるよ。SPAになるよ😘
app({
view: {
"/": (model, actions) =>
<div>
<h1>Home</h1>
<button
onclick={_ => actions.router.go("/about")}>
About
</button>
</div>,
"/about": (model, actions) =>
<div>
<h1>About</h1>
<button
onclick={_ => actions.router.go("/")}>
Home
</button>
</div>
},
plugins: [Router]
});
Lifecycle Methods
仮想DOMのライフサイクルにまつわるイベントをハンドリングできるよ。
onCreate
… ElementがDOMとして構築されたときonUpdate
… Elementの要素が更新されたときonRemove
… ElementがDOMから消える直前
const node = document.createElement("div")
const editor = CodeMirror(node)
const Editor = options => {
const setOptions = options =>
Object.keys(options).forEach(key => editor.setOption(key, options[key]))
const onCreate = elm => {
setOptions(options)
elm.appendChild(node)
}
const onUpdate = _ => setOptions(options)
return <div onCreate={onCreate} onUpdate={onUpdate}></div>
}
Afterword
良さそうなところ
- 小さな開発用ツールなどをつくるときには高速で実装できるし、実行速度的にも◎
- 特にReactでJSX使ってた人はとっつきやすいかも
- 小さなライブラリなので、初めてのOSSコードリーディングにはもってこい
- 同じく、初めてのOSSコントリビュートにはもってこい(?)
ちょっと考えものなところ
- テンプレートファイルを外出しできないとマークアップエンジニアとの分業がしにくいかも
- もう少し細かい単位でライフサイクルのイベントハンドリングをしたくなるかも
- viewの共通部品の定義(Abstract的なもの)とか欲しくなりそう
- SSR対応できる?
一部訳すのをさぼりました。すみません😵
初めて翻訳(の真似事)をしたのですが、自然と海外ドキュメンタリーの吹き替えみたいな声が脳内再生されました。「私ゃ失敗こいちまってさ」的なやつです。ともあれ、とても楽しかったですしHyperAppを好きになれて満足しました。
間違いや指摘箇所があれば@aloerina_までご連絡ください。
Subscribe via RSS