19
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ElmAdvent Calendar 2019

Day 3

Elm SPAの初歩

Last updated at Posted at 2019-12-03

趣味の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をやってみようという方は大仰な初期構成を目指して躓く前に試してみると良いかもしれない。

19
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?