6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

StimulusでjQueryを少しモダンにできるかもしれない

Posted at

Stimulusというフレームワークを先日初めて知りました。
HTMLファーストで面倒なcreate-xxx-appもせずにHTMLを直接いじることでwebアプリケーションを構築できそうです。

以下のようなHTMLがあったとします。

<form method="POST" action="/authenticated">
  <label for="username">username: <input name="username" id="username" type="text"></label>
  <label for="password">password: <input name="password" id="password" type="password"></label>
  <input id="login" type="submit" value="login">
</form>

最初は/authenticatedに認証情報を飛ばしそのまま次のHTMLにあれよこれよされる構成だ
ったのでしょう。
そして時代が立ちフォームはログインさせるだけにそのコントロールは元のページでする
ようになりました。
jQueryはこう書いたことでしょう。

<form>
  <label for="username">username: <input id="username" type="text"></label>
  <label for="password">password: <input id="password" type="password"></label>
  <input id="login" value="login">
</form>
<script>
  function login(name,pass){
  ...
  }
  var name = $('#username').val();
  var pass = $('#password').val();
  $('#login').on('click', function() {
    login(name,pass)
  });
</script>

JavaScriptが装飾以外に使われるようになったのです。
login関数にログインした時の処理は押し込まれるようになりました。
そしてそのまま十数年の時が経ちます。。。

気が付けばフロントエンド周りは素晴らしい進化を遂げました。
当初補助的な言語として扱われたJavaScriptは今やそれ単体でリッチなWebアプリケーションを作れるほどになりました。

ついでにIEもいなくなったので十数年ぶりの改修をしてみようと思います。
とはいえ浦島太郎状態の人たちはnodejsとかnpmとかなにそれの大混乱です。
ここは新しくなったフロントエンドを学ぶ意味合いでStimulusで書いてみましょう。

<form data-controller="login">
  <label for="username">username: <input data-login-target="username" type="text"></label>
  <label for="password">password: <input data-login-target="password" type="password"></label>
  <input data-action="click->login#submit" type="submit" value="login">
</form>
<script>
  function login(name,pass){
  ...
  }
</script>
<script type="module">
  import {Controller, Application} from "https://cdn.skypack.dev/stimulus";
  const app=Application.start()
  class LoginController extends Controller {
    static targets = ['username', 'password'];

    submit() {
      login(
        this.usernameTarget.value,
        this.passwordTarget.value
      )
    }
  }
  app.register('login', LoginController)
</script>

jQueryのHTMLと比較をしてみましょう。
form要素とinput要素に先ほどはなかった属性が追加されています。
form要素にはdata-controller属性、
input属性にはdata-login-target属性とdata-action属性が追加されています。
Stimulusは既存のHTMLにひと手間入れるだけで完成します。
見た目の部分にほとんど気を使わなくていいのは大きなメリットですね。

id属性を削除しましたが、そのまま付加したままでも使えます。loginボタンにtype="submit"を付加しましたが、enterを押すとフォームを送信してくれる機能を、もともとjQueryを使う以前のフォームのように扱えるようにしたかったからです。
これをjQueryでやろうとするとenterキーが押されたことを検知して送信するみたいな処理にしないといけないため省いていました。

jQueryの時代にはなかったHTML5の機能をフルに生かせるようになります。

一方ロジックの部分はどうでしょう。

  var name = $('#username').val();
  var pass = $('#password').val();
  $('#login').on('click', function() {
    login(name,pass)
  });

の5行から

  import {Controller, Application} from "https://cdn.skypack.dev/stimulus";
  const app=Application.start()
  class LoginController extends Controller {
    static targets = ['username', 'password'];

    submit() {
      login(
        this.usernameTarget.value,
        this.passwordTarget.value
      )
    }
  }
  app.register('login', LoginController)

の13行に増えました。
書き方が変わっているだけで要素から値を取得してloginに放り込んでいるだけで、やっていることは変わりません。
ただ後者ではコントローラーという概念を持ち込んでいます。これはjQueryの時代にはな
かったものではないでしょうか。

MVCモデルやクリーンアーキテクチャー、設計手法自体も進化しています。
それらと組み合わせることで現代的なwebアプリケーションに近づくでしょう。

Stimulusのイケてないところ

Stimulusの最大の利点はnodejsを使ったJavaScriptエコシステムを使わなくてもいいことです。
ということはそれらが使えるならStimulusのメリットがなくなるということになります。
create-xxx-appができるならそっち使った方がいいと思います。

targetやvalueが気持ち悪い

static targets = [ "item" ]

static values = {
  url: String
}

で参照や状態を作り出すことはできますがその書き方が

this.itemTarget

this.urlValue

でthis.[名前][機能]になっています。
直感的に考えればthis.Target[名前]になると思うのですがなぜこうなってしまったのでしょう…
調べても出てこなかったので知りたいです。

this問題に向き合う羽目になる

JavaScriptは歴史のある言語です。
それによってthisの扱いは複雑です。
先ほどのコードがもしこうなっていたらどうでしょう

    submit() {
     function login(name,value){
       console.log(
        this.usernameTarget.value,
        this.passwordTarget.value
       )
     }
     login()
    }

これは動きません。
thisがコントローラーをささずlogin関数を指してしまうからです。
これを解決するには無名関数を使いますが、初学者にはなぜ正常に動作しないかわかるこ
とは難しいでしょう。

    submit() {
     const login=(name,value)=>{
       console.log(
        this.usernameTarget.value,
        this.passwordTarget.value
       )
     }
     login()
    }

typescriptと相性悪い

valueには型を入れなければなりません。

  static values = {
    code: String
  }

しかしtypescriptはこれを認識せずvalueが増えるごとに型を書かなくてはなりません。

  declare codeValue: string

二重に管理しなくてはならないため不便です。

細かい仕様がある

HTMLから先に記述すると起こりやすいですが、ケバブケースを使用することはできません

キャメルケースを使用します。

<span data-search-target="camelCase" />
<span data-search-target="do-not-do-this" />

感想

Reactに慣れた体からするとimport from "https://cdn.skypack.dev/stimulus"は画期的でした。
そういえばdenoもURL importしてたなあ。
ブラウザで直接動かすというのも新鮮でした。原点回帰もいいですね。まあ実運用するか
は別問題ですが…

Railsと動作することを念頭に開発しているのでRails使っている方はいかがでしょうか()

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?