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

CMSの編集ロックUI部分で元気に走り回るRiot.js

はじめに

ピクシブ株式会社 Advent Calendar 2016 6日目です。 新卒(16卒)エンジニアの@shocotaです。 会社では主にPHPを、趣味でRuby on RailsでWebアプリを開発しています。

今回は弊社メディアpixivisionの記事編集画面にRiot.jsを導入して、編集ロック機能を実装したことについてお話します。

多言語対応メディアを支える編集画面

pixivisionは現在4ヶ国語で記事を展開しており、各国語での記事を同時に公開することも少なくありません。この編集作業を支援するため、弊社では専用の編集システムを社内で開発しています。

f:id:devpixiv:20161206232954p:plain

多人数で編集される記事にふりかかる困った問題点

pixivisionを支えるために多くの作業者が関わっており、1つの記事上で複数の作業が同時に発生し、他者の編集内容を上書きしてしまう場面が出てきます。 これまでは作業フローの工夫によって衝突を防いできました。しかし、人手が関わるため、どうしても衝突が生じることがあります。そのようなときにはエンジニアが履歴データから頑張って復旧することで対処していました。このような応急処置では、エンジニアの開発も、記事作成者の作業も止まってしまいます。また、クリエイターを応援するべき弊社が、使いづらい編集システムによって、記事作成という一種の創作活動を阻害してしまうことはメンタリティとしてもよろしくありません。

そうして、私は編集ロック機能の開発に踏み切りました。

編集ロック機能の開発

今の状況を打開すべく、複数の編集者が記事を同時に編集できない仕組み、編集ロック機能を開発しようと思いました。 サーバー側の実装は比較的単純で、キャッシュを使って誰がどの言語をロックしているかの情報を保持して返すだけです。

しかし、物事はそうはうまく進みません。フロント側では複雑に絡み合う状態遷移をjQueryでなんとかする必要がありました。

複雑な状態遷移を乗り越えるべく導入したRiot.js

pixivisionのフロントは、jQueryによるDOM操作を駆使して書かれていました。今の書き方では、多様なロック状態に合わせてUIの状態を更新コードをメンテナンスし易く書くのが難しそうだと判断しました。

そこで、今回採用したのはRiot.jsと、RiotControlです。

f:id:devpixiv:20161206233430p:plain

Riot.jsの特徴

Riot.jsはWebページ上のDOM要素の生成と制御を支援するView用のライブラリで、コンポーネント指向を採用するなどReactに大きく影響を受けています。 容量が非常にコンパクトで、APIの数も少なく覚えやすいなどの特徴があります。最も大きな利点はWeb Componentsのように「HTMLのカスタムタグを新しく定義する」思想で作られていることだと考えています。 あるUI要素に関連するJSのロジックやCSSなどを1ファイル内に封じ込めることができるため、コンポーネントごとに管理しやすくなります。その上、<style><script>タグを持った小さなHTML(-likeな)文書として各カスタムタグを記述できます。

<list-items>
  <h3> Fruits </h3>

  <ul>
    <li each={ item in items }>{ item }</li>
  </ul>

  <style scoped>
    h3 {
      color: red;
    }
  </style>

  <script>
    this.items = ['Orange', 'Grape', 'Apple', 'Strawberry']
  </script>
</list-items>

表示結果: f:id:devpixiv:20161206233053p:plain

riot.observe()で、複数タグの協調動作を実現する

編集ロックでは、「編集をはじめる」ボタンを押すことでフォーム上のオーバーレイが消えるなど、複数のUIが状態によって変化する挙動がたくさんあります。

f:id:devpixiv:20161206233258g:plain

しかしRiot.jsには、マウントされた特定のタグのメソッドを外部から直接呼び出す仕組みがありません。その代わりにriot.observe()を利用し、イベントを呼び出すことによって状態の更新処理を発火することにより、複数のカスタムタグを協調して動かすことができます。 ただし、これはきちんと使い方を決めて共有しておかないと、あっという間に処理の流れが追えないコードになってしまいます。実装には、RiotControlを使用しました。

Riot.jsでFluxを実現する軽量ライブラリRiotControl

RiotControlは、Fluxにインスパイアされたriot.observe()を用いる簡素なライブラリです。このライブラリを使用すると、処理の流れをAction・Store・Viewに分離して責務を分けることによって、見通しのよいコードを書くことができます。

Riot.jsにした決め手は、学習コストの圧倒的低さ

今回、Riot.jsとRiotControlを採用した経緯には、私にある程度の知見があったこともあるのですが、以下の点も考慮しました。

  • 今回は機能追加が一番の目的であり、時間をかけてビルド環境をがっつり用意しなくても導入できる
  • pixivisionのエンジニアはJavaScriptを触る機会はフロントエンドエンジニアと比べると少なく、私を含めて最新のJavaScriptの文化にそんなに慣れていない
    • Riot.jsのタグ記法はSmartyでのHTML制御と較べて違和感なく書ける
  • 現在の構成に不満が出たとき、Reactなどの別構成への移行も容易
    • Fluxによってきちんと疎結合を保つようにコーディングしていれば、コンポーネントごとの段階的な移行も可能

編集者にもわかりやすい、状態が的確に反映されるUI

実作業をしている編集者に意見を聞いたところ、かなり好評でした。

  • 編集ロックが導入されたことにより、編集がぶつからなくなった
  • 現在誰が編集してるのか表示され、急ぎで編集を行いたいときは声かけで対応できた
  • エラー時にアラートが飛ぶだけでなく、次の「再読み込み」という行動に誘導され、わかりやすい(下図) f:id:devpixiv:20161206233341p:plain

以上のように、Riot.js導入によって、現在の状態がUIに反映され、編集者に見えるようになりました。jQueryだけで実現しようと思ったら、これほど短時間で実装は終わらなかったと思います。

まとめ

本記事では、既存のプロダクトのUIとロジックをRiot.jsとRiotControlによって部分的に拡張し、置き換える事例について紹介しました。Riot.jsは既存の画面に対して部分的に適用できますし、昨今のJavaScriptのフレームワーク事情に明るくなくても学習コストが少ないために導入しやすいのが特徴です。 既存システムに新しい思想のフレームワークを加えるときは大げさな変更を考えがちですが、まずはミニマルに変更して、チームで学習しながら広げていくのも重要だと考えています。そういう意味では、このライブラリ導入は成功でした。

みなさんもぜひ使ってみましょう。