Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

よろしくおねがいします

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

Stimulus 入門編

Slide 8

Slide 8 text

Stimulus とは? • Basecamp (元 37signals) が作っている JavaScript フレームワーク • すごく雑に言うと他のフレームワーク
 でいうところの Controller しかない • https://stimulusjs.org もぜひ目を通して
 みてください

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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}!` } }

Slide 11

Slide 11 text

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}!` } }

Slide 12

Slide 12 text

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}!` } }

Slide 13

Slide 13 text

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}!` } }

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

サンプルをもう 1 個
PIN: 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") } }

Slide 17

Slide 17 text

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: Copy to Clipboard

Slide 18

Slide 18 text

Action の指定 // clipboard_controller.js import { Controller } from "stimulus" export default class extends Controller { static targets = [ "source" ] copy() { this.sourceTarget.select() document.execCommand("copy") } }
PIN: 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") } }

Slide 19

Slide 19 text

Target の指定 // clipboard_controller.js import { Controller } from "stimulus" export default class extends Controller { static targets = [ "source" ] copy() { this.sourceTarget.select() document.execCommand("copy") } }
PIN: 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") } }

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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}!` } }

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

Stimulus 実践編

Slide 25

Slide 25 text

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 したいケースも無くはない • でもテンプレートの仕組みは用意されていない

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

こんな issue がありました • https://github.com/stimulusjs/stimulus/ issues/41 • “HTML 要素の追加に タグを
 使うのはどうだろうか?” • というわけで タグを使って要 素の追加をやってみました

Slide 28

Slide 28 text

タグとは? • https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ template • HTML の断片を タグの要素として記述することで
 いわゆるテンプレートとして使用することができる • https://www.html5rocks.com/en/tutorials/webcomponents/template/ も参考にしました

Slide 29

Slide 29 text

タグの使い方 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)

Slide 30

Slide 30 text

タグの使い方 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)

Slide 31

Slide 31 text

タグの使い方 UPC_Code Product_Name

Slide 32

Slide 32 text

タグの使い方 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)

Slide 33

Slide 33 text

タグの使い方 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)

Slide 34

Slide 34 text

タグの使い方 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)

Slide 35

Slide 35 text

タグの使い方 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)

Slide 36

Slide 36 text

タグの使い方 UPC_Code Product_Name

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

タグのまとめ • HTML の中にテンプレートとして使いたい
 HTML の断片を埋め込むことができる • ただの HTML なので色々とベンリです

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

タグのネスト • タグは
 ネストすることもできる • 要素の外側もテンプレートに
 しておきたいときに使う • 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)

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

こんな 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…” • まじかよ…

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

実際に使ってみて • フレームワークが構造を用意してくれているのは
 指針になるのですごく分かりやすくて良い • 人数が少ないチームだとコンポーネントに
 分かれているよりもずっと触りやすい • が複雑になってくると大変なので
 テンプレートで頑張りたいときはもう少し
 カンタンなアプローチがあると良いかも

Slide 49

Slide 49 text

雑多なトピック

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

コードスタイルの統一 • Standard JS • No configuration を売りにしている • Standard RB • Rubocop の config ファイルが同梱されている • コードスタイルをレビューする余力がないので
 なるべく自動化できるところはそちらに寄せたい • スタイルの指摘をされるのもあんまり嬉しくないので
 (auto correct でわりと解決できる)

Slide 52

Slide 52 text

まとめ ,

Slide 53

Slide 53 text

お話したこと • Stimlus について簡単に紹介しました • Stimulus と タグとを
 組み合わせるアプローチについて解説しました • ベストプラクティスなどは
 まだ模索しているところです