フロント界隈で一番イケてるのは AngularJS でも React でもなく Riot.js だという話

phi phi on javascript, Riot.js

ここ最近話題になったエントリーですが, どちらも今最もイケてる Riot.js(ライオット・ジェイエス) について全く触れられてなくて悲しくなったので書きました.

はじめに

「webpack インストールしといてー」, 「gulp 導入して watch しながら開発してー」, 「React だから ES6 でコード書いてー」
最近こんな会話が当たり前のように現場で飛び交っています.

フロントエンドの開発者には

  • HTML, CSS に特化している人 (coder, markup engineer)
  • JavaScript は得意だけど CSS はちょっと弱い人 (javascripter)
  • HTML, CSS, JavaScript 全てを統べる者 (frontend engineer)

といった様々なタイプのエンジニアがいます.

近年, 便利なタスクランナーや JavaScript でゴリゴリやるフレームワークが増えてきたことで, それぞれのタイプのエンジニア同士による 『あ・ゆ・み・よ・り』 の意識が低くなってきているように感じています. 個人的には, フロントエンドのプロジェクトは pull して index.html 開けば即何かしら見れるぐらいシンプルであるべきだと考えています.

また, クライアントサイドに "サーバーサイドライクな MVC を持ち込んだり", "CSS のイケてない仕様を命名規則で人力カバーしようとしたり", "AltJS 前提の開発環境だったり" とますますカオスな状況です.

っと, どんどんフロントエンド技術へのハードルが高くなってきている昨今ですが, そんな中それら全てを解決してくれる救世主が現れました.

それが Riot.js です!!

他のライブラリと比較して書こうと思ったのですが, どうしても他のライブラリを批判する内容になってしまったので 公開直前で大幅に削りました.(とはいえ全部削るとタイトルとの整合性がなくなるので下の方に他のライブラリとの比較をまとめて書いてます)

今回は, Riot.js の素晴らしいところにフォーカスを当てて紹介していきたいと思います.

コンポーネント指向 と HTML 準拠なタグ定義

Riot.js においてまず特徴的なのは, コンポーネント指向です.

コンポーネント指向とは

コンポーネント指向とは, データ, 見た目, 振る舞い全てをひとまとめにしたもの(コンポーネント)を定義し, それらを使いまわして開発していくという考え方です.

コンポーネントの種類には, 概念的に大きく分けて2種類あります.

  • モジュール ... パーツの集合体で, サービスに依存する処理やパーツの管理を行うもの
  • パーツ ... サービスに依存せず独立したものでその中で全てが完結し汎用的に使いまわせるもの

うまく必要な要素をモジュールやパーツに切り分けることで作業をキレイに分担することができ, 大規模なプロジェクトでも安全かつ効率良く開発することができます.

ゲーム開発でもよく採用されるように 実装が見た目や動きに依存する ような場合に向いています.

従来の開発方法の場合の問題点

カード要素をクリックしたときに小さくしたい場合があるとします. その場合下記のような処理が必要になります.

  1. クリックした際に発火するイベントに処理を紐付ける(HTML or JavaScript)
  2. 1で登録した処理内で .compact クラスを付与(JavaScript)
  3. .compact クラスが付いている場合 height の値を小さくする(CSS)

このようにちょっとした仕様の変更でも HTML, CSS, JavaScript それぞれの修正が必要になります. 今までのスタンダードな開発の場合 index.html, style.css, main.js といった対応するファイルそれぞれを修正する必要がありました. しかもそれらの修正の影響範囲がぱっと見ただけではわかりません.

Riot.js の場合は違います.

Riot.js によるタグ定義

Riot.js の場合, タグという View, Style, Script を一つにまとめたコンポーネントを定義, マウント(ここでは紐付ける, 展開する的なニュアンス)していく ことでサイトを構築していきます.

簡単な例を見てみましょう.

<card onclick='{output}'>  
  <div class='title'>{title}</div>
  <div class='content'>{content}</div>
  <style scoped>
    :scope {
      display: block;
      background-color: white;
      padding: 0.5rem;
      margin: 0.5rem;
      box-shadow: 1px 1px 2px 0px #aaa;
    }
    :scope.compact {
      height: 22px;
      overflow: hidden;
    }
  </style>

  this.title = opts.title || 'untitled';
  this.content = opts.content;

  this.output = function() {
    this.root.classList.toggle('compact');
  };
</card>  

Riot.js を知らない人でも, ちょっと Web をかじっていれば何となく何をするコンポーネントか分かるかと思います.

ざっくり色分けするとこんな感じですね.

riot tag

カードをクリックしたらコンパクトにするという処理がこのタグ内で完結しています.

見てわかるとおりちっちゃな Web ページを作っているような感じですね. このように今まで作ってきた構造となんら変わらない形でタグを定義することができますし, 新たに学ぶ必要があることはほとんどありません.

下は実際に <card /> を展開したサンプルです.

Demo - タグを定義 - riot.js

選択できるコンパイルのタイミングと言語

もちろんサービスのリリース時にはコードを concat したり uglify, minify したりってことは必要です. ですが開発時にそれらは大抵邪魔です. Riot.js はちゃんとその辺考慮してくれていて, タグを事前にコンパイルするかランタイムでコンパイルするかを選ぶことができます.

大きく分けると下記のパターンがあります.

  • プリコンパイルして js コードに落とし込んだタグを使う
  • .tag ファイルを動的に読み込んでランタイムコンパイルして使う
  • タグを直に HTML 内で定義してランタイムコンパイルして使う

開発時はランタイムにコンパイルする形で進めて, リリース時にプリコンパイルした js ファイルを使うといったことができますね.

また, コンパイル時に View, Style, Script の言語もそれぞれ指定することができます. 下記は対応言語一覧になります.

View

  • html
  • jade or pug(version 3 からは pug のみ)
<script type="riot/tag" template='jade'>  
  ...
</sctipt>  

Style

  • less
<script type="riot/tag">  
  ...

  <style type='less'>
  </style>
</sctipt>  

Script

  • es6
  • babel
  • coffee
  • livescript
  • typescript
  • vanilla
<script type="riot/tag">  
  ...

  <script type='es6'>
  </script>
</sctipt>  

Scoped CSS

SMACSS とか BEM とか意識しなくて ok!

Riot.js は Scoped CSS に対応しているので, コンポーネント単位でネームスペースの閉じた CSS を指定することができます.

<script type="riot/tag">  
  ...

  <style scoped>
    ...
  </style>
</sctipt>  

大規模開発や運営時にこの機能が地味に効いてきます. CSS を修正した際の影響範囲が明確なので改修によるスタイル崩れの頻度が劇的に下がります.

その代わり CSS の使い回しが難しくなりますがその辺はトレードオフですね.

とはいえ, 日々仕様が変わるのがフロントエンドですから CSS を使い回すことを考えるよりもいかに修正しやすい実装にするかを考えたほうが設計上も精神衛生上も良い結果になると思います.

下記は, 実際に Scoped CSS in Riot.js を使ったデモになります

Demo - Scoped CSS - riot.js

シンプルなルーティング

まずは下記のコードを見てください.

<script type="riot/tag">  
  <app>
    <h1>{ title }</h1>
    <content></content>
    <ul>
      <li><a href='#home'>home</a></li>
      <li><a href='#search'>search</a></li>
      <li><a href='#notifications'>notifications</a></li>
      <li><a href='#mypage'>mypage</a></li>
    </ul>

    this.title = opts.title;

    riot.route(function(tagName) {
      tagName = tagName || 'home';
      riot.mount('content', tagName);
    });

    riot.route.start(true);
  </app>

  <home>
    <h2>Home デース!</h2>
  </home>
  <search>
    <h2>Search デース!</h2>
  </search>
  <notifications>
    <h2>Notifications デース!</h2>
  </notifications>
  <mypage>
    <h2>Mypage デース!</h2>
  </mypage>
</script>  

実はこれ, たったこれだけのコードで SPA サイトになっています.

home, search, notifications, mypage とそれぞれタグを定義して, url が変わる度にそれに対応するタグを content にマウントすることで SPA を実現しています.

また, ルーター周りの処理がタグの定義と独立しているのでルーティングに 専念出来る 点も大きな魅力の一つです.

別途ルーティングに絞ったエントリーを書く予定なのでここでは詳しく説明しませんが, Riot.js を使えば簡単に SPA サイトを作ることができるということを感じてもらえればと思います.

下記は上で紹介したコードのデモです. 下のリンクをクリックすると動的にコンテンツが変わります.

Demo - Routing - riot.js

他のライブラリとの比較

上で削ったと書きましたが批判にならない程度に抑えめでまとめました. よかったら参考までに.

AngularJS との比較

これすげー, データバインディングめちゃめちゃ便利じゃん! directive とか filter とかなんか HTML 自体を拡張してる感じがして楽しい!!

っというのが AngularJS の最初の印象でした. ですが使っていくにつれて, 色々と問題が起きました.

まず, 独自のルールや記法がかなり多いです. そのため AngularJS に則った実装をしないといけないので単純に学習コストが高い && 独特な流儀を学んでも他の技術を使うときにその経験を活かしづらいです.(Angular Way 問題)

また, 小規模であれば良いんですが大規模になってくると「共通で使いたい変数どうすんの?」 とか「コントローラって継承しちゃっていいの?」 といった様々な疑問が出てきます.

それらに対して問題自体を解決することはできるのですが, しっくりくるベストプラクティスがありません(単純に私の技量不足疑惑...)

それと MVC ベースな点も問題の一つです. MVC は, 一見コードがスッキリするし役割ごとにファイルを分けられるので分業できて良いと思いがちです. が, フロントエンドの開発においては完全にミスマッチです.

フロントエンドの場合, 実装がデータ, 見た目, 振る舞いそれぞれと密に関わり合っているので完全に切り分けるのは不可能もしくは冗長になります. そのため, 何か機能追加や仕様変更があった場合, あちこちにちらばった関連するコードを見つけ修正する必要があり, 単純に修正のコストが跳ね上がります. 実際に AngularJS で大規模なプロジェクトを開発, 運用された経験がある方は激しく共感して頂けるのではないでしょうか.

Angular2 というコンポーネント指向(Riot.js, React 寄り)になった新しいバージョン(もはや別物w)も出ているので
今採用するメリットはほぼないかと思われます.

Angular2 との比較

コンポーネント指向になりましたー\(^o^)/ 後方互換を捨てたのは個人的に好感持てます.

ですが, シンタックスは相変わらずの Angular Way です... コンポーネント指向になったもののコンポーネントの定義も独自の書き方満載なので学習コストはかなり高いです.

あと, 前のバージョン以上にガチガチのフレームワークになってしまったので, こちらの Quickstart を見てもらったら分かる通り全然クイックスタートで始められる環境じゃないです.

また, 言語が TypeScript や ES6 前提というアプローチは, 未来に向けては良いと思います. ですがビルド環境やデベロッパーツールが成熟するまでは, 使っていく中で色々と躓くことも多いでしょう.

Angular2 に関しては, 正直1回しか採用したことがないので引続き使い倒してここに追記もしくは別途比較エントリーを書こうと思っています.

React との比較

正直 Riot.js と似ています. っといいますか Riot.js 自身が React like を謳っており, かなり影響を受けています.

思想自体は同じですが, React は Javascript セントリックなのに対し Riot.js は HTML セントリックです.

そのため React は, jsx(※Adobe の ExtendScriptでも DeNA の jsx でもありません) という js 内に xml を自然な形で書き込める言語を使うことで js 完結でコンポーネントを定義できるようにしています.

一見よさ気ですが, どうしても学習コストはかかりますし実際に書いていくと js 内に html, css を書いていくのはかなり違和感があります.

「jsx はオプショナルだから使わなきゃいいじゃん」という意見もあると思いますが, 文字列で html を js 内に書くのは現実的にありえないので実質 jsx は必須といえるでしょう.

何度か React を採用してサービスを作りましたが, 色々構築しないといけないわりに, やれることはほとんど Riot.js でシンプルに実装できることばかりなので Riot.js で良いじゃんという結論に私は至りました.

Vue.js

一時期ハマってました. シンプルで使いやすいので, jQuery オンリーで書いてたページとかに後でデータバインディングを導入したいといった場面では Vue.js が最適です.

小規模な一画面だけのページとかであればすごく便利ですし, 実際に Electron で Vue.js を採用していくつかアプリを開発しました.

ですが, 大規模なプロジェクトになると Vue.js のみで完結させるのは難しいです. View に特化してるからというのもあるのですが routing やコンポーネント化には他のプラグイン(一応公式で色々ある)が必要になります.

結論

この エントリー 風に書くと

  • 主要ライブラリは Riot.js ... 一通り網羅されてる
  • アプリケーションフレームワークは Riot.js ... イケてる
  • ルーティングは Riot.js ... 最低限で ok!
  • UIフレームワークは Materialize ... いきなり出てきましたが個人的にオススメです!
  • HTTP Request は jQuery ... ぶっちゃけ通信できれば生 xhr でもなんでも良いです!
  • 記述は JavaScript ... alt 系も微妙な感じだし es5 で不便ないので
  • ビルド環境は gulp ... 最低限で十分
  • 文法・構文チェックは しない ... 余裕あればやるけど...
  • テストも しない ... フロントエンドでテスト?w 動作デバッグあるのみでしょ!

両論あるとは思いますが, これまで大量に SPA サイトやアプリを作ってきてこの構成がベストだと感じていますし今のところ上手くまわっています.

ちなみにこの構成で作ってる SPA サイトの一つが 『Runstant』 です. 他にもたくさんあるのですが, 仕事で作ったやつだったりするので公開していいか確認してから追記したいと思います.

おわりに

フロントエンドは技術の流行り廃れが激しく何を選択すれば良いのか迷っている方, たくさんいると思います.

ただ, そこで 「Google が開発しているから AngularJS だ!」 とか 「Facebook が開発しているから React だ!」 とか「有名な人が押しているから Vue.js だ!」 とかそういった選び方はせず, ちゃんと自分で触ってみて良し悪しを判断して欲しいなと思います.

全部触っている時間なんてない!! なんて言う人もいると思います. ですが, そこそこのフロントエンド経験者であれば基本的な部分だけならどれも半日程度で学べるはずです.

だとしても学習コストは高いかもしれません. が, 長い目で見るとそれが一番自分にとっても一緒に開発するメンバーにとっても良い選択ができるはずです.

ちなみに私が Web 開発でフレームワーク, ライブラリを選択する際の判断基準は

  • 既存の技術から逸脱していないか
  • ルールが柔軟か
  • Hello, world! までが手軽か
  • ぱっと見とっつきやすいか
  • それを学ぶことで他の環境にも活かせるか
  • 理念があるか
  • コードにこだわりがあるか
  • ゲームプログラミングっぽく書けるか(ゲーム畑出身なので💦)

といった感じですね. 使い方は OSS であれば中身を見て程度わかるので流行っているかどうかはあまり重視しないです.

私はこれまで様々なライブラリを使って SPA サイト, iOS/Android アプリを開発してきました. それぞれ一長一短ある中で, Riot.js が一番どのタイプのエンジニアにも優しく, 使えば使うほど手に馴染む良いライブラリだと感じました.

Riot.js は, HTML の正統進化 だ! ともに暴動を起こし, フロント界隈に革命と秩序を再び.

ってことでこのエントリーをきっかけに, Riot.js に興味を持ってくれた方, 触ってみようと思う方が増えれば幸いです.

触ってみようと思ってくれた方はこちら(『とりあえず試してみたいって方のための Riot.js 入門 | phiary』 )をどうぞ