LoginSignup
40
13

More than 3 years have passed since last update.

elm-spaで温まりシングルページアプリケーションの厳しい寒さを乗り越える

Last updated at Posted at 2019-12-12

YAMA_DSC2132_TP_V.jpg

先月、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-spa.png

elm-liveが起動してページが表示されました。ふむふむ。上のhomenowhereというリンクを踏むとページが切り替わることが確認できます。ちゃんと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と書かれたページが表示されました。なるほどです。

elm-spa2.png

ページ上部のリンクは、どのページでも共通で表示されるみたいですね。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-spa3.png

なお、elm-spa add staticだと、titleviewしかない動きのないページが作成されるみたいです。

src/Pages/KinIro.elm
page : Page Params.KinIro Model Msg model msg appMsg
page =
    Spa.Page.static
        { title = always "KinIro"
        , view = always view
        }

それに対しelm-spa add elementだと、initupdatesubscriptionも備えた動きのあるページが作れるようになるみたいです。

$ npx elm-spa add element Mosaic
src/Pages/Mosaic.elm
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.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ではグローバルなupdatesubscriptionが指定できるようになっていますが、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みたいな付加的な機能を提供している多機能寄りのツールだという感じがします。

この種のツールがどこまでうまくいくのかはまだまだ未知数だと思いますので、興味のある方は使ってみてぜひ知見を共有していただければと思います。

参考文献


  1. このトピック、みんなやけにテンション高いな! 

  2. 共通のsubscriptionが欲しければ、どこかでそのsubscriptionを定義して、各ページでその共通のsubscriptionをインポートして使います。一度は筆者もいったんAlchelmyにそのような機能を実装したのですが、シンプルにするためにあえて削りました。 

  3. トランジションが不可能というわけではなくて、Alchelmy側で機能を提供するのではなく、アプリ側で自力で実装するようになってます 

40
13
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
40
13