Hyperappを試してみた。

Hyperappとは?

1Kという常識を覆すほどの超軽量のJavascriptフレームワーク Reactからいらないものを極限まで削ぎ落とした感じ。 驚くことにみんな大好きQiitaで採用されているとのこと。

2018 年は Hyperapp の年だ - Qiita

ここら辺に作成者さんの思想などが詰まっているのだけれども、一言で羨ましい。 これを作れる技術力も、コミュニティの温かさも、設計思想も。 OSSとしてこういうものを作れるエンジニアになりたいよね。

他、紹介記事はここら辺。

ReactやVueなどのフレームワークは個人で何かを作るとなった際には、正直大きすぎるので 速度重視の場合にはこういうものを使いたいよねということで今回目をつけました。あとは新しい物好きの興味本位。

Setup

超絶怒涛のウルトラスーパー簡単セットアップ。

<script>
import {h, app} from "https://unpkg.com/hyperapp?module"
</script>

View

JSX的な書き方が使える。Hyperappからimportしたhという 関数がComponentに対応している感じ。ほぼほぼhtmlの書き心地と同じである。

<!doctype html>
<html>
  <head>
    <link rel="stylesheet" href="https://zaceno.github.com/hatut/style.css">
    <script type="module">

// -- IMPORTS --

import {h, app} from "https://unpkg.com/hyperapp?module"


        
// -- ACTIONS --


                
// -- VIEWS ---

        
        
// -- RUN --

app({
  node: document.getElementById("app"),
  view: () => h("div", {id: "app", class: "container"}, [
    h("div", {class: "filter"}, [
      " Filter: ",
      h("span", {class: "filter-word"}, "ocean"),
      h("button", {}, "\u270E")
    ]),
    h("div", {class: "stories"}, [
      h("ul", {}, [
        h("li", {class: "unread"}, [
          h("p", {class: "title"}, [
            "The ",
            h("em", {}, "Ocean"),
            " is Sinking!"
          ]),
          h("p", {class: "author"}, "Kat Stropher")
        ]),
        h("li", {class: "reading"}, [
          h("p", {class: "title"}, [
            h("em", {}, "Ocean"),
            " life is brutal"
          ]),
          h("p", {class: "author"}, "Surphy McBrah"),
        ]),
        h("li", {}, [
          h("p", {class: "title"}, [
            "Family friendly fun at the ",
            h("em", {}, "ocean"),
            " exhibit"
          ]),
          h("p", {class: "author"}, "Guy Prosales")
        ])
      ])
    ]),
    h("div", {class: "story"}, [
      h("h1", {}, "Ocean life is brutal"),
      h("p", {}, `
        Lorem ipsum dolor sit amet, consectetur adipiscing
        elit, sed do eiusmod tempor incididunt ut labore et
        dolore magna aliqua. Ut enim ad minim veniam, quis
        nostrud exercitation ullamco laboris nisi ut aliquip
        ex ea commodo consequat.
      `),
      h("p", {class: "signature"}, "Surphy McBrah")
    ]),
    h("div", {class: "autoupdate"}, [
      "Auto update: ",
      h("input", {type: "checkbox"})
    ])
  ]),
})
    </script>
  </head> 
  <body>
    <div id="app"></div>
  </body>
</html>

State

素晴らしいのが状態管理システムを持つところ。 初期状態を定義してviewに引数として渡してやると、利用できるようになる。 更新箇所はReactのように限定はできないようだが、小さいApplicationでは全然気にはならない。

app({
        node: document.getElementById("app"),
        init: {
          filter: "ocean",
          reading: "113",
          stories: {
            "112": {
              title: "The Ocean is Sinking",
              author: "Kat Stropher",
              seen: false,
            },
            "113": {
              title: "Ocean life is brutal",
              author: "Surphy McBrah",
              seen: true,
            },
            "114": {
              title: "Family friendly fun at the ocean exhibit",
              author: "Guy Prosales",
              seen: true,
        },
        view: state => ...

      });

Action

actionもお手の物。Reactとほぼほぼ同じである。 任意の値を引数として渡す場合には、関数を渡している箇所を配列に変更して 追加でpayloadとして渡してやればいいだけ。ものすごくシンプルである。

const StartEditingFilter = state => ({...state, editingFilter: true})

const Filter = state => h("div", {class: "filter"}, [
  "Filter:",
  h("span", {class: "filter-word"}, state.editingFilter),
  h("button", { onClick: StartEditingFilter }, "\u270E")
])

Effect

APIとの通信関連の箇所の様子。Http通信後に帰ってきた値をpayloadとして引数のactionを 実行する。本当にReactとかの必要な機構をミニマムでとってきた感がする。 これらの仕組みを1Kになってるのがすげえよなぁ...

import {Http} from "https:/unpkg.com/hyperapp-fx@next?module"

const StopEditingFilter = state => [
  {
    ...state,
    editingFilter: false,
  },
  Http({                                                                            // <---
    url: `https://zaceno.github.io/hatut/data/${state.filter.toLowerCase()}.json`,  // <---
    response: "json",                                                               // <---
    action: GotStories,                                                             // <---
  })    
]

初期stateとしても使えるみたい。

init: FetchStories({
    editingFilter: false,
    autoUpdate: false,
    filter: "ocean",
    reading: null,
    stories: {},
  }),

まとめ

フロントを作る際に必要な箇所を詰め込んだフレームワークという印象。 複雑なものを作る際にはもっと色々と必要になるが、個人プロジェクトなどを作る際は全然使えそう。 学習コストなども非常に低そうなので、色々と小回りが効きそうなフレームワーク 。 何より小さい。すごい。

今年はOSSに貢献することを目標にしているので、こういうところからはじめられたらいいなと。