ginza.rb#64 でstimulusを紹介したときに使った資料です
https://ginzarb.doorkeeper.jp/events/81390
stimulus— ginza.rb#64— @willnet
View Slide
とは— Stimulus: A modest JavaScript framework for the HTMLyou already have.— Basecamp製のJSフレームワーク— 現在のバージョン1.1.0(2018/8/23)
試用— webpackなどでプリコンパイルするのを推奨— cloneしてサッと試せるお試し用リポジトリがある— stimulusjs/stimulus-starter: A humble blank slatefor a modest JavaScript framework— rails使いなら rails new --webpack=stimulusyourappname でも環境作れるのでそっちでもよさそう— 公式チュートリアル的なのがあるので、それを見ながら手を動かすとよいのでは
特徴— react や vue.js とは志向がぜんぜん違う— データバインディングとかVirtual DOMとか、なにそれ美味しいの?って感じ— あくまでサーバが吐くHTMLと仲良くするためのフレームワーク— turbolinks friendly— サーバサイドの人が片手間でやるフロントエンドで、vue.jsなどを導入する余裕がない…みたいなときに導入するのが良いのでは、という気がする— 現行のjsを整理するために使う
どういうときに便利そうか— 通常RailsのJSはファイルごとにそれっぽく関数やイベント登録などが整理されるけれど、特に強制力はない— なのでフラットにイベントが登録されたり、関数が登録されたりする可能性がある— 例えば users.js に user とあまり関係ない関数が登録されたりする— DOMのclass属性とjsが紐付けられる事が多いけど、cssのために使うclassなのかjsで使うclassなのか分かりづらい— stimulusを使うとDOMに controller, target, action を割り振ることができ、規約によって関数群を整理できる、というのがstimulusのキモの一つ(だと思う)
どういうときに便利そうか— turbolinksを使っているときに、特定のページでだけ実行したい処理を書くのが面倒— 特定のDOMがあるかチェックして、なかったらreturnする、みたいな処理を毎回書く— だるい
こういうやつ$(document).on('ready turbolinks:load', () => {const target = $('.condition_transfer_on')if (target.length === 0) {return}// .condition_transfer_on͕͋ͬͨ߹ͷॲཧ— stimulusを使うと「controllerがページ表示されたタイミング(connectされたタイミング)でなにか処理を実行する」みたいなのが楽にかける
controller, target, action とはstimulus 公式のトップページを見ながら解説するとわかりやすそうStimulus: A modest JavaScript framework for the HTML youalready have.
HTMLのコードGreet
jsのコードimport { Controller } from "stimulus"export default class extends Controller {static targets = [ "name", "output" ]greet() {this.outputTarget.textContent =`Hello, ${this.nameTarget.value}!`}}
結果名前を入力してGreet!をクリックすると、右側に名前を含んだ文字列が出力される
HTMLとjsの対応付け— data-controllerの値とjsファイル名が紐付いている— data-actionの値とメソッドが紐付いている— data-targetの値とstatic targets = [] で定義された値が紐付いている— static targets = ["name"] などとすると、this.nameTarget でDOMの要素を扱うことができるようになる
stimulusの大まかな機能はこれだけ
完?
それぞれもうちょっと詳しく
controller— html中のdata-controllerに合わせて、紐付いた名前のcontrollerをインスタンス化し、DOMと紐づけてくれる— 例えば であればhello-controller.jsが自動的に読み込まれる— ファイル名にアンダースコアを使うこともできる— date_picker_controller.js -> date-picker— 参考: https://stimulusjs.org/handbook/installing#controller-filenames-map-to-identifiers
複数のコントローラを一つのDOMに紐付けることができる
コントローラを複数のDOMに紐付けることができるOneTwoThreeli要素それぞれに、別々のコントローラのインスタンスが割り当てられる
action…— イベントの名前(click)— コントローラ名(garraly)— アクション名(next)をそれぞれ指定する
eventオブジェクトactionとして定義されたメソッドに、eventオブジェクトが引数として渡ってくるimport { Controller } from "stimulus"export default class extends Controller {next(event) {// …}}
eventオブジェクト— event.type でなんのイベントなのかが返る(例: 'click')— event.targetでどのDOM発祥なのか返る— event.currentTargetでイベントリスナが登録されているDOMが返る
イベントの名前— DOM要素によっては省略できる— 例えばa要素ならclickがデフォルトで省略可能
同じDOMに複数イベント登録できる
targetAction以外のDOMとjsの紐づけはtargetでやる
htmlcontroller໊.target໊ で指定する
jsimport { Controller } from "stimulus"export default class extends Controller {static targets = [ "query", "errorMessage", "results" ]// …}— statis targets に配列でtargetになる要素の名前を渡す— すると後述のプロパティが生える
プロパティ例えばqueryという名前のtargetを指定すると次のプロパティが自動で生える— this.queryTarget— 最初にマッチしたDOMが返る— this.queriesTarget— マッチしたDOMが全部返る— this.hasQueryTarget— DOMが存在しているのかbooleanで返る
一つのDOMに複数のtargetを指定できる…
Data Mapsdata-content-loader-url="/messages">— 状態をどこで持つのか、という話— 他のjsフレームワークはjsで持つけど、stimulusjsはDOMで持つ— html側からjs(その逆も)になにかデータを渡すとき、data-ίϯτϩʔϥ໊-σʔλ໊で渡すというのがstimulusjsでのお作法
js// controllers/content_loader_controller.jsimport { Controller } from "stimulus"export default class extends Controller {connect() {fetch(this.data.get("url")).then(/* … */)}}— this.data.get("url")でdata-content-loader-urlの値を取得できる
その他のメソッド— this.data.set(key, value)— DOMの方にデータを設定する— this.data.has(key)— booleanで、そのkeyのdataが存在しているのか返す— this.data.delete(key)— keyに紐づくdataを消す
注意点— dataはNumberでアサインしても、getするとStringで返ってくる— とくに型変換とかはしてないもようthis.data.set("count", 1)this.data.get("count") // "1"
Lifecycle CallbackcontrollerのインスタンスがDOMに割り当てられたときなどにhookしてメソッド実行をさせることができるimport { Controller } from "stimulus"export default class extends Controller {connect() {// …}}
メソッド— initialize— controllerのインスタンスが作られたタイミング— connect— DOMに紐付いたタイミング— ドキュメント上にdata-controllerを持つDOMが現れたらconnectされる— disconnect— DOMとの紐づけがなくなったタイミング— data-controllerを持つDOMが消されたり、data-controller属性自体が消されたりしたらdisconnect
DOMが存在する/しないで自動でcontrollerが紐付けられるというのが— turbolinksフレンドリーってことっぽい?
インスタンスは使い回される— disconnectしたあとにまたconnectされるケースでは、インスタンスが使い回される— つまりconnectやdisconnectは複数回呼ばれる可能性がある
まとめ
メリット— 学習コストがめっちゃ低い— 一日あれば使えるようになるのでは?— vannilajsやjQueryで作っているサービスから移行しやすい— 試してみた結果やめたくなった場合も比較的やめやすそう
デメリット— vannilajsから移行しても「単にメソッドやイベントを整理した」以上にはならない— マジカルな何かですごいことになる、ということはない— できることが限定的— SPAとか無理(そういうのはturbolinks使って擬似的にやればいいじゃん、という考え)— JSON APIを叩いて云々、とかは考えてない— クライアントサイドでDOMを組み立てる、なんて大変でしょ?全部HTMLでやればいいんだよ
以上