この投稿は Increments Advent Calendar 2017 の14日目の記事だよ。Hyperapp という JavaScript ビューライブラリを自作しているので、その説明と作った理由について話す。
Hyperapp ができるまでのプロセスや、どんな価値観で作ったかなどを書く。新しいフレームワークを作る時の参考になれば嬉しい。
Hyperapp とは?
Web アプリのフロントエンド用 JavaScript ライブラリ。React, Preact, Vue といった代表的なものよりもずっと小さく、1 KB という超軽量サイズ。他のライブラリに依存することなく使えて、さらにスピードもある
Elmアーキテクチャーに基づいてて、アプリケーション設計はElmやReact、Reduxと似てるけど、ボイラープレートは少ないし、TypeScriptにも対応して、とにかくシンプル。
Elmと違って、Update関数なしで、Actionsというコンセプトを使ってる。Actionsは普通の関数だ。Action呼び出すと、自動的にStateを更新して、Viewは再レンダリングされる。
裏側ではHyperappがコードにActionsを挿入して、アプリケーションをレンダリングするタイミングを指示する。
ちなみに、上の例ではJSXを使って説明してるけど、HyperappはJSXに依存してはいない。
ビルトインの**h
**関数を使って、こういふうにViewを書くこともできる。
const view = (state, actions) => h("h1", {}, "Hello World!")
他のオプションもあって、Hyperx, T7、そして、公式な@hyperapp/html. 例えば、さっきの例を @hyperapp/htmlで書き直してみようー
import { app } from "hyperapp"
import { div, h1, button } from "@hyperapp/html"
const state = { count: 0 }
const actions = {
down: value => state => ({ count: state.count - value }),
up: value => state => ({ count: state.count + value })
}
const view = (state, actions) =>
div([
h1(state.count),
button({ onclick: () => actions.down(1) }, "–"),
button({ onclick: () => actions.up(1) }, "+")
])
const main = app(state, actions, view, document.body)
Hyperappのもっと詳しい話はドキュメントをご覧ください!👋
歴史の話
今年の初めごろ、ただ自分のために Hyperapp を作った。最初は趣味みたいな気持ちだったけど、パブリックしてすぐに GitHub のトレンディングにランクインしてびっくりした。
それで「ああ!みんなにも便利に感じてもらえるんだ!」と思って、もっとブラッシュアップして、VDOM も作って、実際に Qiita でも使うことになって、 Hyperapp はいつの間にか自分にとって、メインプロジェクトになっていた。
なぜ Hyperapp を作ろうと思ったのか?
以前から、既存のライブラリはちょっと大きすぎて、100 ポケットある豪華なジャケットみたいだと思っていた。それもいいけど、もっとシンプルな 2 ポケットのジャケットはないのか??自分はそれで充分だ。
仕事で React や たまに Redux などを使ってフロントページを作っていたのだけど、その中身は非常に複雑で、本当にこんなに色々しないといけないのだろうか、もっと簡単にならないだろうか?と考えているうちに、そうだ、自分が満足したいものを自分で作ろうと思った。
そこで他のVDOM (まず、Matt-Esch/virtual-dom、そして paldepind/snabbdom) ライブラリを使いながら、それらが「どんな技術を使うか」をよく観察して、自分だったらどんなアーキテクチャにするか?と考えて、作ってみた。
難しかったところ
VDOMの部分は非常に難しかった。実は VDOM は既存の何かを使おうと思ってた。でもその時自分が使っていた VDOM は、すでにメンテナーがほとんどケアしなくなっていて、僕もPRしたけど反応がなく、何か問題が起きた時に困るなぁと気になっていた。
ミニマルなVirtual DOMエンジンも欲しかったけど、なかった。じゃあー僕のプロジェクトにシナジーがある VDOM も作ろう。それに、同じプロジェクトで VDOM も アーキテクチャー も提供できれば、他のライブラリに依存しない、自分が完全にコントロールできるフレームワークになる!と考えた。
ちなみに、Snabbdomというお世話になったVDOMの実装をよく読解して詳しく勉強して、自分が欲しいシンプルな DOM を作ってみた。これがのちの Picodom だ。(その時はまだ (~900B) くらいだったから、Hyperapp の中に入ってた。)
Picodomとは?
Picodom は VDOM でページを操作する 1 KB の JS ライブラリで、API は 2 つのみ。だからもちろん Picodom をそのまま使って SPA を作ることもできるけど、あくまでも軽量化を優先したスタイル。
どちらかというと、ユーザーが自分の VDOM 系ビューライブラリ(P/React とか、Inferno, Riot, Hyperapp など)を作るのに便利に使えるということが、Picodom の存在意義だと思っている。Picodom をベースにしたオリジナルビューライブラリや、VDOM 系ツールとか、ユーザーが楽しく簡単に作れたら嬉しいと思う。
一番大事にしたこと
もっとも重要視していたのは、エレガントな API と 1 KB というサイズ。
エレガントであることは、全て整っていて、気持ち良く使えることはまず大切。だけどそれだけでなく、Hyperapp では「簡単な方法で複雑な問題を解決したい」と考えていた。エキサイティングな機能や特徴を入れれば、もっとパワフルにできるけど、それをやらないのはとにかく簡単な方法で!っていうことを最優先したからだ。Hyperapp をどのくらいシンプルに作れるかどうかを、自分に対して挑戦している。
1 KB にこだわった理由、1 KB であることの利点
フロントエンドのアプリケーションサイズは 5 KB でも十分に小さいと思う。でももっと小さくすることにこだわったのは、別に一番になりたいとかじゃない(笑)。プロジェクトのコードを 1 KB 程度でまとめられれば、JS のプログラマーなら 1、2 時間ですべてのコードを読める、仕組みを完全に理解できる。
オープンソースの世界ではソースはパブリックだからもちろん誰でも読めるけど、読めるのと「全部把握している」のはまた別だ。使いながらも、いつもブラックボックスの部分がある。でも 300行なら、バックヤードの全てを理解した上で使うことができる。それを実現したかった。
今後について
今の課題は「このままの簡単な API と 1 KB のサイズで、今より多くの問題を解決すること」だ。シンプルさと 1 KB にこだわって作ったことは十分説明したけど、ジャケットの例なら、新しい持ち物が増えても、ポケットを増やすんじゃなく 2 ポケットのままどうにか解決したい。
これが本当に難しい!だから細かいブラッシュアップをずっとしてる。API の一番細かいところを常に直してる。だけどそろそろバージョン 1 をリリースしたいと思っている。
最後に
オープンソースのメンテナとして何度も格闘してきたことがある。ユーザーは常に自分が求めていることと機能リクエストを優先して、それを必要としないユーザーにかかるコストを軽視しがちだ。
ライブラリの作者は、プロジェクトの長期的な健康状態と、ユーザーのニーズを満たす欲求とのバランスを常に取らないといけない。 これはマジで難しい。なぜなら、ライブラリがだんだん太くなっていくことを先に予測することは難しいからだ。
だからHyperappもなんども変更があった。ベストな方法のために、その時はかっこいいと思った機能も消すことになったり、APIが何回も新しくなったりと色々あった。けど、やっと満足ができた!それで、Hyperapp 1.0 をリリースしました!