Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Just Tried Stimulus

Just Tried Stimulus

Shohei Umemoto

June 27, 2019
Tweet

More Decks by Shohei Umemoto

Other Decks in Technology

Transcript

  1. Stimulus をちょっと
    使ってみました
    Shohei Umemoto (@cafedomancer)
    Rakuten Tech Sapporo WEBエンジニア MeetUp@札幌 #5 (June 27, 2019)

    View Slide

  2. 自己紹介
    • Shohei Umemoto (@cafedomancer)
    • 社会人4 年目です (たぶん…)
    • 開発では Ruby / Rails をよく使います
    • fish shell / fzf/ neovim が好きです

    View Slide

  3. 会社紹介
    • Enishi Tech Inc. (@enishitech)
    • いろいろなことをやっています
    • お客様といっしょに考えます
    • Ruby / Rails だけの会社というわけでは

    特にありません (でも大切にしています)

    View Slide

  4. よろしくおねがいします

    View Slide

  5. 話すこと
    • Stimulus の紹介
    • Stimulus の実践的な使い方 (?)
    • その他いろいろ気になっていること

    View Slide

  6. 話さないこと
    • JavaScript の細かい話
    • Stimulus のセットアップやインストール
    • Webpacker (Webpack) の (以下同文)
    • Stimulus を使う上での Tips みたいなの (in-depth なので)

    View Slide

  7. Stimulus 入門編

    View Slide

  8. Stimulus とは?
    • Basecamp (元 37signals) が作っている
    JavaScript フレームワーク
    • すごく雑に言うと他のフレームワーク

    でいうところの Controller しかない
    • https://stimulusjs.org もぜひ目を通して

    みてください

    View Slide

  9. Stimulus の 3 つの要素
    • 出てくるのは 3 つだけ
    • Controller / Action / Target
    • 実際にコードを読んでみよう!

    View Slide

  10. Stimulus のコード




    Greet




    // hello_controller.js
    import { Controller } from "stimulus"
    export default class extends Controller {
    static targets = [ "name", "output" ]
    greet() {
    this.outputTarget.textContent =
    `Hello, ${this.nameTarget.value}!`
    }
    }

    View Slide

  11. Controller の指定




    Greet




    // hello_controller.js
    import { Controller } from "stimulus"
    export default class extends Controller {
    static targets = [ "name", "output" ]
    greet() {
    this.outputTarget.textContent =
    `Hello, ${this.nameTarget.value}!`
    }
    }




    Greet




    // hello_controller.js
    import { Controller } from "stimulus"
    export default class extends Controller {
    static targets = [ "name", "output" ]
    greet() {
    this.outputTarget.textContent =
    `Hello, ${this.nameTarget.value}!`
    }
    }

    View Slide

  12. Action の指定




    Greet




    // hello_controller.js
    import { Controller } from "stimulus"
    export default class extends Controller {
    static targets = [ "name", "output" ]
    greet() {
    this.outputTarget.textContent =
    `Hello, ${this.nameTarget.value}!`
    }
    }




    Greet




    // hello_controller.js
    import { Controller } from "stimulus"
    export default class extends Controller {
    static targets = [ "name", "output" ]
    greet() {
    this.outputTarget.textContent =
    `Hello, ${this.nameTarget.value}!`
    }
    }

    View Slide

  13. Target の指定




    Greet




    // hello_controller.js
    import { Controller } from "stimulus"
    export default class extends Controller {
    static targets = [ "name", "output" ]
    greet() {
    this.outputTarget.textContent =
    `Hello, ${this.nameTarget.value}!`
    }
    }
    // hello_controller.js
    import { Controller } from "stimulus"
    export default class extends Controller {
    static targets = [ "name", "output" ]
    greet() {
    this.outputTarget.textContent =
    `Hello, ${this.nameTarget.value}!`
    }
    }




    Greet







    Greet




    // hello_controller.js
    import { Controller } from "stimulus"
    export default class extends Controller {
    greet() {
    this.outputTarget.textContent =
    `Hello, ${this.nameTarget.value}!`
    }
    }
    // hello_controller.js
    import { Controller } from "stimulus"
    export default class extends Controller {
    greet() {
    `Hello, ${this.nameTarget.value}!`
    }
    }

    View Slide

  14. ねっ?カンタンでしょ!
    • これだけです
    • ちょっとデモをします
    • stimulus-starter を使います

    View Slide

  15. Stimulus の 3 つの要素 (再)
    • Controller
    • JavaScript で動作させたい単位となるもの
    • Action
    • HTML のイベントが発生したときに実行されるもの
    • Target
    • HTML からの入力と HTML への出力の対象となるもの

    View Slide

  16. サンプルをもう 1 個


    PIN: type="text" value="1234" readonly>

    Copy to Clipboard


    // clipboard_controller.js
    import { Controller } from "stimulus"
    export default class extends Controller {
    static targets = [ "source" ]
    copy() {
    this.sourceTarget.select()
    document.execCommand("copy")
    }
    }

    View Slide

  17. Controller の指定
    // clipboard_controller.js
    import { Controller } from "stimulus"
    export default class extends Controller {
    static targets = [ "source" ]
    copy() {
    this.sourceTarget.select()
    document.execCommand("copy")
    }
    }
    // clipboard_controller.js
    import { Controller } from "stimulus"
    export default class extends Controller {
    static targets = [ "source" ]
    copy() {
    this.sourceTarget.select()
    document.execCommand("copy")
    }
    }


    PIN: type="text" value="1234" readonly>

    Copy to Clipboard


    View Slide

  18. Action の指定
    // clipboard_controller.js
    import { Controller } from "stimulus"
    export default class extends Controller {
    static targets = [ "source" ]
    copy() {
    this.sourceTarget.select()
    document.execCommand("copy")
    }
    }


    PIN: type="text" value="1234" readonly>

    Copy to Clipboard


    // clipboard_controller.js
    import { Controller } from "stimulus"
    export default class extends Controller {
    static targets = [ "source" ]
    copy() {
    this.sourceTarget.select()
    document.execCommand("copy")
    }
    }

    View Slide

  19. Target の指定
    // clipboard_controller.js
    import { Controller } from "stimulus"
    export default class extends Controller {
    static targets = [ "source" ]
    copy() {
    this.sourceTarget.select()
    document.execCommand("copy")
    }
    }


    PIN: type="text" value="1234" readonly>

    Copy to Clipboard


    // clipboard_controller.js
    import { Controller } from "stimulus"
    export default class extends Controller {
    static targets = [ "source" ]
    copy() {
    this.sourceTarget.select()
    document.execCommand("copy")
    }
    }
    // clipboard_controller.js
    import { Controller } from "stimulus"
    export default class extends Controller {
    copy() {
    this.sourceTarget.select()
    document.execCommand("copy")
    }
    }

    View Slide

  20. クリップボードのサンプル
    • こちらもデモをしてみます
    • 同じく stimulus-starter を使います

    View Slide

  21. Stimulus の pros & cons
    • とてもシンプルなので分かりやすく覚えることが少ない
    • Reference を見てみると分かると思います
    • HTML と JavaScript との関係がひと目で分かる
    • このあと説明します
    • まわりで使っている人が見当たらない
    • ノウハウがまだ無さそう
    • ゴリゴリの SPA みたいなのを作るのには向いてなさそう

    View Slide

  22. jQuery を使ったコードとの比較
    $('div.hello button.hello-greet’)
    .click(function() {
    const $name =
    $(this).siblings('input.hello-name')
    const $output =
    $(this).siblings('span.hello-output')
    $output.text(`Hello, ${$name.val()}!`)
    })



    Greet







    Greet




    import { Controller } from "stimulus"
    export default class extends Controller {
    static targets = [ "name", "output" ]
    greet() {
    this.outputTarget.textContent =
    `Hello, ${this.nameTarget.value}!`
    }
    }

    View Slide

  23. Stimulus のまとめ
    • Controller しかない JavaScript フレームワーク
    • 要素としては Controller/ Action / Target の 3 つだけがある
    • ちょっとした動きモノを作るのに向いてそう

    View Slide

  24. Stimulus 実践編

    View Slide

  25. HTML に対する Stimulus の考え方
    • “Stimulus is a JavaScript framework with modest ambitions. It doesn’t
    seek to take over your entire front-end—in fact, it’s not concerned
    with rendering HTML at all.”
    • でも HTML を render したいケースも無くはない
    • でもテンプレートの仕組みは用意されていない

    View Slide

  26. HTML を render したいケース
    • API から JSON を受け取ってそれを HTML で表示したい
    • ユーザーのアクションに基づいて要素を追加したい

    View Slide

  27. こんな issue がありました
    • https://github.com/stimulusjs/stimulus/
    issues/41
    • “HTML 要素の追加に タグを

    使うのはどうだろうか?”
    • というわけで タグを使って要
    素の追加をやってみました

    View Slide

  28. タグとは?
    • https://developer.mozilla.org/en-US/docs/Web/HTML/Element/
    template
    • HTML の断片を タグの要素として記述することで

    いわゆるテンプレートとして使用することができる
    • https://www.html5rocks.com/en/tutorials/webcomponents/template/
    も参考にしました

    View Slide

  29. タグの使い方



    UPC_Code
    Product_Name












    let t = document.querySelector('#productrow')
    let tb = document.querySelector('tbody')
    let clone = document.importNode(t.content, true)
    let td = clone1.querySelectorAll('td')
    td[0].textContent = '1235646565'
    td[1].textContent = 'Stuff'

    tb.appendChild(clone)

    View Slide

  30. タグの使い方



    UPC_Code
    Product_Name












    let t = document.querySelector('#productrow')
    let tb = document.querySelector('tbody')
    let clone = document.importNode(t.content, true)
    let td = clone1.querySelectorAll('td')
    td[0].textContent = '1235646565'
    td[1].textContent = 'Stuff'

    tb.appendChild(clone)

    View Slide

  31. タグの使い方



    UPC_Code
    Product_Name



    View Slide

  32. タグの使い方



    UPC_Code
    Product_Name












    let t = document.querySelector('#productrow')
    let tb = document.querySelector('tbody')
    let clone = document.importNode(t.content, true)
    let td = clone1.querySelectorAll('td')
    td[0].textContent = '1235646565'
    td[1].textContent = 'Stuff'

    tb.appendChild(clone)

    View Slide

  33. タグの使い方



    UPC_Code
    Product_Name












    let t = document.querySelector('#productrow')
    let tb = document.querySelector('tbody')
    let clone = document.importNode(t.content, true)
    let td = clone1.querySelectorAll('td')
    td[0].textContent = '1235646565'
    td[1].textContent = 'Stuff'

    tb.appendChild(clone)

    View Slide

  34. タグの使い方



    UPC_Code
    Product_Name












    let t = document.querySelector('#productrow')
    let tb = document.querySelector('tbody')
    let clone = document.importNode(t.content, true)
    let td = clone1.querySelectorAll(‘td')
    td[0].textContent = '1235646565'
    td[1].textContent = 'Stuff'

    tb.appendChild(clone)

    View Slide

  35. タグの使い方



    UPC_Code
    Product_Name












    let t = document.querySelector('#productrow')
    let tb = document.querySelector('tbody')
    let clone = document.importNode(t.content, true)
    let td = clone1.querySelectorAll('td')
    td[0].textContent = '1235646565'
    td[1].textContent = 'Stuff'

    tb.appendChild(clone)

    View Slide

  36. タグの使い方



    UPC_Code
    Product_Name



    View Slide

  37. TODO リストのサンプル
    • よくあるやつ
    • ちょっとデモをします

    View Slide

  38. タグの pros & cons
    • ただの HTML なので読みやすい
    • ノンプログラマーの人がいじる時にもやりやすい
    • だいたいのブラウザーで使える
    • https://caniuse.com/#feat=template
    • Fetch API と組み合わせて良い感じに動かせる
    • https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
    • Internet Explorer でうまく動かないケースがある
    • このあと説明します

    View Slide

  39. タグのまとめ
    • HTML の中にテンプレートとして使いたい

    HTML の断片を埋め込むことができる
    • ただの HTML なので色々とベンリです

    View Slide

  40. Stimulus を使ってみた
    プロジェクトのおはなし

    View Slide

  41. デモをします
    • 架空のプロジェクトのおはなし (だと思って聞いてください)

    View Slide

  42. Stimulus と Turbolinks
    • “Stimulus pairs beautifully with Turbolinks to provide a complete
    solution for fast, compelling applications with a minimal amount of
    effort.”
    • Turbolinks と組み合わせても問題なく使用することができます
    • モバイル向けのページでは Turbolinks を使っておくと良さそう

    View Slide

  43. Internet Explorer で動かすには?
    • Stimulus のPolyfill が用意されています
    • https://www.npmjs.com/package/
    @stimulus/polyfills
    • も Polyfill が用意されています
    • https://github.com/webcomponents/
    polyfills/tree/master/packages/
    template
    • Fetch API も (以下同文)
    • https://github.com/github/fetch

    View Slide

  44. タグのネスト
    • タグは

    ネストすることもできる
    • 要素の外側もテンプレートに

    しておきたいときに使う
    • Internet Explorer でも問題なく

    動作させておきたい









    et outer = document.querySelector('#outer')
    let list = document.importNode(outer.content, true)
    let ul = list.querySelector('ul')
    let inner = list.querySelector('#inner')
    for (let i = 1; i <= 3; i++) {
    let item = document.importNode(inner.content, true)
    let li = item.querySelector('li')
    li.textContent = 'List Item ' + i
    list.querySelector('ul').appendChild(item)
    }
    let todos = document.querySelector('#todos')
    todos.appendChild(list)

    View Slide

  45. ぼくの どこいった
    • ちょっとデモをします
    • ネストされた を使います

    View Slide

  46. こんな issue がありました
    • https://github.com/webcomponents/
    template/pull/
    39#issuecomment-430617066
    • “HTML parser of IE will drop
    directly, you won't find anything of your
    template…”
    • まじかよ…

    View Slide

  47. どうしようもないので…
    • createElement でがんばりました
    • の意味よ…

    View Slide

  48. 実際に使ってみて
    • フレームワークが構造を用意してくれているのは

    指針になるのですごく分かりやすくて良い
    • 人数が少ないチームだとコンポーネントに

    分かれているよりもずっと触りやすい
    • が複雑になってくると大変なので

    テンプレートで頑張りたいときはもう少し

    カンタンなアプローチがあると良いかも

    View Slide

  49. 雑多なトピック

    View Slide

  50. フロントエンドのパッケージ管理
    • Stimulus で使うものは Yarn で入れています
    • Stimulus で使うもの以外は Rails Assets で入れています
    • Webpack (er) に乗せるとハマりそう… (見たことある)
    • Sprocket 4 でどうにかできるのかな?

    View Slide

  51. コードスタイルの統一
    • Standard JS
    • No configuration を売りにしている
    • Standard RB
    • Rubocop の config ファイルが同梱されている
    • コードスタイルをレビューする余力がないので

    なるべく自動化できるところはそちらに寄せたい
    • スタイルの指摘をされるのもあんまり嬉しくないので

    (auto correct でわりと解決できる)

    View Slide

  52. まとめ ,

    View Slide

  53. お話したこと
    • Stimlus について簡単に紹介しました
    • Stimulus と タグとを

    組み合わせるアプローチについて解説しました
    • ベストプラクティスなどは

    まだ模索しているところです

    View Slide