読者です 読者をやめる 読者になる 読者になる

ボクココ

サービス開発を成功させるまでの歩み

JavaScript SPA 周りの議論で出た私なりの答え

Rails JavaScript

ども、@kimihom です。

ここ最近、ずっと React.js か jQuery かみたいな話題が定期的に持ち上がってきている。事実、私も最近の JS ライブラリの人気に疑問を呈した人物の一人である。この記事を書いたのも1年前か。

なんだかんだで SPA から jQuery に戻った話

そして、現在も jQuery(厳密に言えば jquery-rails) でアプリケーションを書いている。そしてその決断を特に後悔したことはない。本記事では、よく聞くReact とかに移りたい理由をもとに、私なりの回答を記すことにする。

jQuery の DOM 地獄から解放されたい

「jQuery で DOM 操作なんてもうしたくないんです」これこそ React や Angular を使う最大の理由であることが多いかと思う。確かに jQuery の DOM 機能は頑張ればいろいろなことができるので、無法地帯になりやすいという性質を持っている。

だが私が思うのは、 jQuery で複雑な DOM 操作なんてする必要がないということである。そもそもの発想として、JavaScript で状態管理をするという考え方自体に問題があるのではないか。状態を保っている根幹はサーバーにあるDBであり、それを無理にフロント側で持とうとするから大変なことになる。Web の特徴として、"常にオンラインであること前提でいい"というのがあると思う。オフライン対応の Webアプリケーションを作るという話はなしということにとりあえず同意していただいたとして、そうすれば常に最新情報が取ってこれる前提で物事を考えてよいことになる。

となると大活躍する Rails の View オプションは、remote: true だ。これを a タグや button タグ、 form タグなどで追加するだけで Ajax 化できる。さらにそのイベントは ajax:successajax:beforeSend, ajax:error などで結果をキャッチできる。

するとどのような実装ができるかというと、レンダリングは常にサーバーサイドで行うことができる。jQuery でやるべきことは、 Ajax でリクエストを送り、帰って来た HTML をそのまま html(response.body) と言った感じで描画すればいいのだ。そうすれば面倒なDOM操作なんて一切必要なく、 Rails を使っているのであれば html.erb で動的なレンダリングを実現できる。今回のサンプルは Turbolinks を使わない場合のパターンだが、 Turbolinks でもほぼ同等のことが可能だ。Turbolinks を使えばデフォルトで HistoryAPIも備わっているので、戻るボタンも簡単に実現できる。ちなみに私は自前で History API を使って実装した。

Rails を使っている人たちはぜひ一度、なぜ Rails は jQuery を今でも採用し続けているのか、なぜ Controller を生成した時にCSS, JS ファイルも一緒にできるのかを考えてみて欲しい。私なりの答えを書くとRails のレールに乗りながら SPA を作るなら、以下のようなコードになるのではないかと思っている。頭の中で書いただけなので動作保証はしないので注意。

- layout
<div id="base">
   <%= link_to "Ajax!", remote: true, class: "getUsers" %>
    <div class="wrapper"></div>
</div>

- controller
class UsersController < ApplicationController
  layout false

  def index
    @users = User.all
  end
end

- view
<div id="userCtrl">
  <ul>
    <% @users.each do |u| %>
        <li><%= u.name %></li>
     <% end %>
  </ul> 
</div>

- css
#userCtrl {
  # userCtrl だけに適用する CSS
}

- js
$(function() {
  $(document).on("ajax:success", '#userCtrl .getUsers', function(q, data, xhr){
    $("#base .wrapper").html(data);
  });
});

私はこの開発方法を見つけた時、実に美しいと感じた。それぞれの影響を最小限に抑えつつ、 Rails の View を完全に活用して jQuery としての SPA を実現できた、と思った。そして今もなおこの方法で DOM 操作をほとんど行わず、 Rails の View をうまく活用することで Rails のレールに乗っている。こうすれば Rails のレールに乗りながら SPA を簡単に作れる。DOM なんてのは一切出てこないし、フロントエンドでの状態管理も存在しない。複雑な JavaScript コードとはおさらばできる。

Rails で AssetPipeline を使わなかったり、 jQuery じゃなくて React-Rails みたいなものを使っている方は、上記の jquery-rails の使い方を知った上で そのような選択をしたのだろうか? 自ら進んで Ruby on Rails からの レールを外れるという選択をした方には、それなりの苦労が必ず発生する。 Rails の思想と反する行為をしているわけだからね。

スマホとかタブレット、IoT 機器とかと統一した APIを中心としたアーキテクチャにしたい

私もこの考え方がすごい好きだった。 Web ってのはあくまで端末の1つに過ぎず、 API がすべての中心となりサービスの根幹となるものであると考えていた。確かにその考え方だと上記方法は利用することができず、 SPA フレームワークを使わないといけない。

ただ、 Web とその他で圧倒的に違う点が一つある。

それが先ほど申し上げた「Webはオンラインであることが保証できている」という点である。だからこそ常に最新のHTMLコードは ブラウザ側でなくサーバー側で持っていられるようになるわけだ。これがスマホとかだとそうはいかないので、スマホ側でうまくアプリないDBとかに保存して描画したりする必要が出てきてしまう。

API に関して言えば、そんなに機能が複雑になることはないだろう。だから そこはそもそも Rails である必要もないだろうし、別で作るべきだと思っている。同時接続とか多くなるだろうし。

終わりに

今回の話は Rails に限った話であり、例えばそもそもバックエンドがシンプルな Node.js の Express などを使っているのであれば、 SPA を選択してフロントエンドゴリゴリで頑張るという考え方もあると思う。本記事はあくまで Rails にレールを乗って SPA を作るための提案の1つである。