趣味のWebアプリでサーバサイドレンダリングベースでElmを部分的に使うというのをやっていたがSPAはロクにやったことがない。
Elm SPAというと何かと、
このelm-spa-exampleを参考にしましょうという情報を見るのだけど正直難しすぎて挫折すると思う (した) 。
どうにもページごとにPageモジュールを作るというのが入門の壁になっている気がする。
Elm Guideでも
ブログ投稿のWebアプリケーションを作っているとすると、私なら次のようなモジュール構成で作り始めると思います。
Main
Page.Home
Page.Search
Page.Author
といったことが書いてあってSPAではPageモジュールを切らないといけないのかなと先入観を持ってしまう。
(このすぐ下に 予め計画を練ってはならない
と書いてはあるが・・・)
しかし個人的にはSPAの場合でもMainモジュールオンリーで書き始めて、必要を感じた段階でモジュールを切れば良いんじゃないかとも思う。
というわけで最低限の構成を組んでみる。
Elm SPAで最低限必須な知識は
-
Browser.application
モードを使う - URLを変更する
- URL変更イベントを検知してなにかする
の3点。
決まりごと
URL が変わるたびに新たな HTML を読み込むのを避けるため、Browser.elementやBrowser.documentを使ったプログラムを作成する代わりに、Browser.applicationを使うといいでしょう。
SPAをする場合は Browser.application
を設定する。
また、Modelに Browser.Navigation.Key
を持たせておく。
type alias Model =
{ key : Nav.Key -- Browser.Navigation.Key
, route : Route
}
ナビゲーション『キー』(Key)は、URL を変更する(pushUrlのような)ナビゲーションコマンドを生成するのに必要です。Browser.applicationでプログラムを作成したときだけKeyを取得することができ、プログラムが URL の変更を検出する用意があることを保証します。
キーの存在理由については正直腹落ちしてないのだが、一旦これらは決まりごととして受け入れる。
URLの変更を指示する
Browser.Navigation.pushUrl
関数の第1引数に Browser.Navigation.Key
第2引数にURLを渡す。
リンクをクリックすると onUrlRequest
イベントが発火するので、そこで上記を実行する。
update msg model =
case msg of
LinkClicked urlRequest ->
case urlRequest of
Browser.Internal url ->
( model, Nav.pushUrl model.key (Url.toString url) )
Browser.External href ->
( model, Nav.load href )
URLの変更直後の検知
URL変更直後のタイミングで onUrlChange
イベントが発火する。
そこでURLを読み取り、UpdateでModelの状態を変化することで描画も意のままにできる。
type Route
= Top
| About
| NotFound
このようなURLパスと合致するRoute型を用意しておき、
routeParser : Parser (Route -> a) a
routeParser =
oneOf
[ Url.Parser.map Top Url.Parser.top
, Url.Parser.map About (Url.Parser.s "about")
]
parseUrl : Url.Url -> Route
parseUrl url =
let
parsed =
parse routeParser url
in
case parsed of
Just route ->
route
Nothing ->
NotFound
これらRoute型の補助関数を定義しておくというのがよくある手法の一つのようだ。
今回は下記を参考にした。
UPDATE内で下記のように利用する。
UrlChanged url ->
let
route =
parseUrl url
in
( { model | route = route }
, Cmd.none
)
完成形
当然規模にもよるが、このようにメインモジュール一本でSPAを構築することは趣味の小さなWebアプリ程度ならまず問題ないと感じた。
これからElm SPAをやってみようという方は大仰な初期構成を目指して躓く前に試してみると良いかもしれない。