先月、elm-spaというツールがDiscourseで発表されて話題になり1、先日筆者が参加したイベントでもこのツールについての紹介がありました。Elmでシングルページアプリケーションを作ると、退屈なボイラープレートを書かされてそれなり面倒なのですが、それを解決してくれる便利なツールのようです。どんなものなのか気になったので、筆者も試してみることにしました。ちなみに、アイキャッチ画像は平湯温泉です。
(※野暮と思いつつ恐縮ながらこのネタを解説させて頂くと、シングルページアプリケーションはSPAと略されますが、"spa"とは英語で温泉の意でして、そこから温泉の画像なのであります)
とりあえずインストール
とりあえずnpm install
しました。
$ npm install elm-spa
$ npx elm-spa
usage: elm-spa <command> [...]
commands:
init [options] <path> create a new project at <path>
options:
--ui=<module> the module your `view` uses (default: Element)
examples:
elm-spa init your-project
elm-spa init --ui=Element your-project
build <path> generate pages and routes
examples:
elm-spa build .
add static <module> create a new static page
sandbox <module> create a new sandbox page
element <module> create a new element page
component <module> create a new component page
examples:
elm-spa add static AboutUs
elm-spa add element Settings.Index
help print this help screen
examples:
elm-spa help
elm-spa wat
elm-spa huh?
まだドキュメントはほとんどなくREADMEも薄いので、Discourseの投稿などを見ながらの手探りです。CLIのヘルプを見ながら勘で動かしてみます。どうやらelm-spa init
でプロジェクトをスキャフォールディングできるっぽいのでやってみます。
$ npx elm-spa init hello
elm-spa created a new project in:
~/test/spa-test/hello
run these commands to get started:
cd ~/test/spa-test/hello
npm start
...
それっぽいプロジェクトファイル群が生成されました。起動してみます。
$ cd hello
$ npm start
elm-liveが起動してページが表示されました。ふむふむ。上のhome
とnowhere
というリンクを踏むとページが切り替わることが確認できます。ちゃんとSPAになっているようです。
ページを追加してみる
elm-spa add
というコマンドがあるので使ってみます。おそらくこのコマンドで新たなページを追加するのでしょう。
$ npx elm-spa add static kin-iro
kin-iro doesn't look like an Elm module.
Here are some examples of what I'm expecting:
elm-spa add static Example
elm-spa add static Settings.User
$ npx elm-spa add static KinIro
elm-spa created 1 file:
~\test\spa-test\hello\src\Pages\KinIro.elm
指定する名前はモジュール名でなければいけないみたいです。これでsrc/Pages/KinIro.elm
というファイルが追加されて、http://localhost:1234/kin-iro
にアクセスするとKinIro
と書かれたページが表示されました。なるほどです。
ページ上部のリンクは、どのページでも共通で表示されるみたいですね。src/Layout.elm
というファイルにこのあたりのview
が書いてあるみたいなので、このヘッダ部分をカスタマイズするにはsrc/Layout.elm
を編集すればいいみたいです。サンプルはelm-uiで書かれているようで、私はちょっと慣れないです。
viewHeader : Route -> Element msg
viewHeader currentRoute =
row
[ spacing 24
, paddingEach { top = 32, left = 16, right = 16, bottom = 0 }
, centerX
, width (fill |> maximum 480)
]
[ viewLink currentRoute ( "home", routes.top )
, viewLink currentRoute ( "nowhere", routes.notFound )
, viewLink currentRoute ( "kin-iro", routes.kinIro ) -- これを追加!
]
なお、elm-spa add static
だと、title
とview
しかない動きのないページが作成されるみたいです。
page : Page Params.KinIro Model Msg model msg appMsg
page =
Spa.Page.static
{ title = always "KinIro"
, view = always view
}
それに対しelm-spa add element
だと、init
やupdate
、subscription
も備えた動きのあるページが作れるようになるみたいです。
$ npx elm-spa add element Mosaic
page : Page Params.Mosaic Model Msg model msg appMsg
page =
Spa.Page.element
{ title = always "Mosaic"
, init = always init
, update = always update
, subscriptions = always subscriptions
, view = always view
}
ふむふむ。ここまで普通に動かせました。
どんなファイルが自動生成されているの?
各ページを取りまとめる役目のファイルは、elm-stuff/.elm-spa/Generated/*.elm
に自動で生成されて配置されているようです。elm-stuff
に配置されていることからわかるとおり、これらのファイルはユーザは通常は触らないファイルでしょう。たとえば、elm-stuff/.elm-spa/Generated/Routes.elm
は次のような内容になっていました。ルータが自動的に構成されていることがわかります。モジュール名がケバブケースに変換されたものがパス名として使われるようです。
-- ROUTES
type alias Routes =
{ kinIro : Route
, notFound : Route
, top : Route
}
routes : Routes
routes =
{ kinIro =
Generated.Route.KinIro {}
, notFound =
Generated.Route.NotFound {}
, top =
Generated.Route.Top {}
}
parsers : List (Parser (Route -> a) a)
parsers =
[ map routes.kinIro
(s "kin-iro")
, map routes.notFound
(s "not-found")
, map routes.top
(top)
]
さいごに
ElmでSPAを作ってみた人はみんな同じことを考えるようで、筆者(hiruberuto)もAlchelmy という似たような目的のツールを作ったことがあります。ちょうど昨年のアドベントカレンダーの記事のネタがまさにこれでした。ツールの目的としてはほとんど同じだと思ってよさそうです。なぜこのようなツールが欲しくなるのかについては、筆者のこの以前の記事が参考になるかと思います。
あえて違いを挙げるとすれば、次のような感じでしょうか。
- elm-spaではモジュール名からパスのパーサが自動生成されており、モジュール名に
Dynamic
というキーワードを含めるとそれがパラメータになるようです。alchelmyではモジュール名とパスは無関係で、ページごとに好きなようにパーサを書けるのでパラメータも普通のSPAと同じように扱えますし、完全に自由です - Alchelmyではページ間を遷移したときに
Session
というデータを受け渡しができますが、elm-spaではそのような機構がないようです - elm-spaではグローバルな
update
やsubscription
が指定できるようになっていますが、Alchelmyには各ページごとにしかありません2 - elm-spaはCLIのほかにElmのライブラリとしても機能を提供します。Alchelmyはライブラリとしては何も提供しません。例えば、elm-spaは
Spa.Transition
というページ遷移時のトランジションを定義する機能を提供しています。alchelmyはその種の機能を提供していません3 - elm-spaは
Platform.worker
を使ってElmで書かれています。AlchelmyはPureScriptで書かれています
なんとなくですが、alchelmyはrtfeldman/elm-spa-exampleのようなSPAの仕組みに忠実に従いつつ最小限度だけ拡張するに留めたシンプル寄りのツールであり、それに対してelm-spaはSpa.Transition
やグローバルなinit
/update
みたいな付加的な機能を提供している多機能寄りのツールだという感じがします。
この種のツールがどこまでうまくいくのかはまだまだ未知数だと思いますので、興味のある方は使ってみてぜひ知見を共有していただければと思います。
参考文献
- Elm-spa: a tool for building single page apps
- 豊かすぎる源泉を出し惜しみなく注ぎ込み平湯館の岩露天風呂 アイキャッチ画像。行ったことはないんですが写真が綺麗だったので
- aratama/alchelmy 手前味噌
- 複数ページのSPAをElmで書くときのボイラープレート部分をalchelmyで自動的に†錬成†する 手前味噌2