RailsとStimulusで
簡単フロント開発

Kosuke Adachi
@fuyu77
2018.11.22 Meguro.rb#21

自己紹介

  • 前職はプログラミンしないSE
    →退職してRailsを勉強
    →2018年7月にCaSy入社
  • 大学の専攻は国文学(短歌)
  • 無職期間にRailsで短歌投稿サイトUtakataをリリース

やりたいこと

個人開発の既存のRailsプロダクトに
JSフレームワークを入れたい

ReactやVue.jsは設計が難しい…

  • 状態管理は?
  • ルーティングは?
  • SPAにするか、Turbolinksを活用するか?
  • 既存コードとの整合性は?

試行錯誤の結果…


Vue.js導入前

Vue.js導入後

つらい

Stimulusの発見

Stimulusとは

  • DHHがCTOを務めるBasecampが開発
  • Turbolinksに対応
    (Turbolinks作者のsstephensonが開発を主導)
  • モダンJSフレームワークの多くがサーバーから取得したJSONを元にHTMLを構築することを主眼とするのに対し、Stimulusはサーバーが用意したHTMLを操作することを主眼とする

既存のRailsアプリへのStimulusの導入

  1. yarnとwebpackerをインストール
  2. $ rails webpacker:install:stimulus
  3. <%= javascript_pack_tag 'application' %>をHTMLのheadタグ内に追加

Stimulusの基本三要素

  • data-controller:controller名を指定する
  • data-target:操作したい要素を指名する
    controller.target
  • data-action:イベントと起動するメソッド名を指定する
    event->contoroller#method

Hello, World!

html
						
hello_controller.js
						import { Controller } from "stimulus"

						export default class extends Controller {
							static targets = [ "name", "output" ]

							greet() {
								this.outputTarget.textContent = `Hello, ${this.nameTarget.value}!`
							}
						}
					

Lifecycle Callbacks

  • initialize():最初に1回だけ呼び出される
  • connect():controllerとDOMが接続する度に呼び出される
  • disconnect():controllerとDOMが切断される度に呼び出される

実行順序

html
						
hello_controller.js
						import { Controller } from "stimulus"

						export default class extends Controller {
						
							initialize() {
								console.log("initialize")
								// initialize
								// connect
								const element = document.getElementById("hello")
								element.remove()
								// disconnect
								document.body.appendChild(element)
								//connect
							}
						
							connect() {
								console.log("connect")
							}
							
							disconnect() {
								console.log("disconnect")
							}
						}	
					

Data APIで状態管理

html
						
  • this.data.has("index"):controllerの要素がdata-hello-indexを持っていたらtrue, 持っていなかったらfalse
  • this.data.get("index"):data-hello-indexの値をString型で返す
  • this.data.set("index", index):data-hello-indexに値をセットする

スライドショー

html
						
🐵
🙈
🙉
🙊
css
						.slide {
							display: none;
						}
						
						.slide.slide--current {
							display: block;
						}
					
slideshow_controller.js
						import { Controller } from "stimulus"

						export default class extends Controller {
							static targets = [ "slide" ]
						
							initialize() {
								this.showCurrentSlide()
							}
						
							next() {
								this.index++
							}
						
							previous() {
								this.index--
							}
						
							showCurrentSlide() {
								this.slideTargets.forEach((el, i) => {
									el.classList.toggle("slide--current", this.index == i)
								})
							}
						
							get index() {
								return parseInt(this.data.get("index"))
							}
						
							set index(value) {
								this.data.set("index", value)
								this.showCurrentSlide()
							}
						}
					

まとめ

  • 個人開発の小規模Railsプロダクトとの相性はバッチリ!
  • Controller単位でJSを整理できるので見通しが良さそう
  • ビジネスで使うには低機能過ぎるか…?

疑問点

  • connect()disconnect()が具体的にどう役立つのかつかめなかった