Basecampメンバーによる新しいJavaScriptフレームワーク"Stimulus"を試してみた

家族からおせちづくりの招集がかかるまでに書けるかどうかテスト :runner:
GitHubでフォローしている[@amatsuda](https://github.com/amatsuda)さんがStarをつけていて、気になったので試してみたことをまとめてみました。
執筆時点でv1.0ではありませんので、理解した上で読み進めてください。

## Stimulusとは

[Stimulus](https://github.com/stimulusjs/stimulus)をひとことでいうと、HTMLに `data-controller` 属性を付与することで、HTMLをコンポーネント単位でJavaScriptオブジェクトに紐づけることのできるフレームワークです。

[Basecamp](https://basecamp.com/)メンバーによって開発されており、主要開発者にはSprocketsやTurbolinks作者の[sstephenson](https://github.com/sstephenson)氏がいます。
BasecampのDOMも見てみましたが、Stimulusが実際に使われているようでした。
[README](https://github.com/stimulusjs/stimulus/blob/43273ae7a14a7adcfdec2953469039a9a60d91a6/README.md)にもありますが、Basecampで導入されていることからも予想がつくとおりTurbolinksとも共存可能です。

## コード

Getting Startedがリポジトリに、Boilerplateがstimulus-starterというリポジトリにあるので、合わせて見てください。

- [The Stimulus Handbook](https://github.com/stimulusjs/stimulus/blob/a2e2164e0d8cdc4fbc896dd6f720beb9ac315f10/handbook/README.md)
- [Stimulus Starter](https://github.com/stimulusjs/stimulus-starter)

まず、次のようにコンポーネントのルートに `data-controller` 属性を付与します。

```html
<div data-controller="hello"></div>
```

次に `hello_controller.js` として次のようなコントローラを書きます。

```js
import { Controller } from "stimulus"

export default class extends Controller {
  connect() {
    console.log("Hello, Stimulus!")
  }
}
```

`connect()` はコントローラがHTMLとひもづいた際に呼ばれる関数で、Reactのライフサイクルでいう `ComponentDidMount()` のようなイメージです。
このコンポーネントをもつページがJavaScriptとともに呼ばれたら、ログに `Hello, Stimulus!` と表示されます。

### アクション

Stimulusでは、たとえばボタンのアクションとしてコントローラの関数を指定できます。
次のコードは、ボタンのクリック時にコントローラの `greet()` を実行しています。

```html
<div data-controller="hello">
  <input data-target="hello.name" type="text">
  <button data-action="click->hello#greet">Greet</button>
</div>
```

```js
import { Controller } from "stimulus"

export default class extends Controller {
  greet() {
    const name = this.targets.find("name").value
    console.log(`Hello, ${name}!`)
  }
}
```

### 3つの属性

上記の例にも登場しましたが、Stimulusには次の3つの属性があり、これらをHTMLに書いていくことになります。

- `data-controller`: コンポーネントの名前
- `data-action`: イベントのトリガ
- `data-target`: コントローラからハンドルする対象

### CoC

上の例では、 `data-controller` として `hello` という名前をつけ、 `hello_controller.js` という名前でファイルを作成しましたが、この対応はアプリケーション側で行う必要があります。

Stimulus StarterではWebpackを用いていますが、 `@stimulus/webpack-helpers` の `autoload()` という関数により、任意のディレクトリ下のファイル名から動的にコントローラとひもづけているようです。
このあたりはRailsの思想と同じですね。

- [stimulus/packages/@stimulus/webpack-helpers/index.ts](https://github.com/stimulusjs/stimulus/blob/aa075344f2b6806124c560bee67485382bb07007/packages/%40stimulus/webpack-helpers/index.ts)
- [stimulus-starter/src/index.js](https://github.com/stimulusjs/stimulus-starter/blob/1cabcbdd665a1185bd4cebd19551841dcdd92556/src/index.js)

Webpackを使わなくてもStimulusを使うことはできますが、その際は手動でStimulusにコントローラを認識させる必要があるようです。

- [Installation Guide](https://github.com/stimulusjs/stimulus/blob/e073e9496f2242856bbd03eb05e5b24d0f7c9383/INSTALLING.md)

## おわりに

以上の例より、たとえばアプリケーションにいいねボタンを実装するとき、そのボタンに対応したJavaScript側の処理を単一のクラスに集約できるので、フロントエンドをクリーンに保てるのでは、と想像できます。

Stimulusは、READMEにもあるとおり、すべてのフロントエンドフレームワークの代替となることは目的としておらず、HTMLをJavaScriptにより拡張するためのフレームワークのようです。

導入対象としては、Ruby on Railsを用い、ReduxやVue.js、Angularなどのフレームワークを使わず、Turbolinksを有効にする前提のアプリケーションにおいて、少ないコストで効率的に開発できるのではないでしょうか。