SHOWROOMの新規開発にHyperappを採用した話
こんにちわ、エンジニアのきむらです。現在は主にフロントエンドを担当しています。
今回は、SHOWROOMの新規機能開発にHyperappというJavaScriptフレームワークを採用したので、そのお話をします。
はじめに
SHOWROOMは、ターゲットのプラットフォームをPC / iOS / Androidとしていますが、 iOS / Androidではアプリ内でWebViewコンポーネントを利用しWebページを表示している画面がまだ数多くあります。
既存のページは以下のJavaScriptフレームワーク / ライブラリを使用しています。
- JQuery(https://jquery.com/)
- Underscore.js(https://underscorejs.org/)
- Backbone.js(http://backbonejs.org/)
- Marionette.js(https://marionettejs.com/)
数年前に流行ったBackbone.jsからなるクライアントサイドMVCを構成するためのモジュール群です。 ですが、今の世の中の流れは完全に仮想DOM一色、SHOWROOMもこの流れに乗り より機能の変更に柔軟に耐えうる、低コストかつ高速描画の可能なフレームワークを採用することにしました。
< 乗るしかない このビッグウェーブに!
仮想DOMとは
仮想DOMは、VirtualDOM、VirtualNode、VDOM、VNodeなどとも呼ばれます。 もうすでに数年前から仮想DOMに関しての記事は山程出ているため、いまさらこの記事で紹介するまでもないのですが
ものすごく簡単にご説明すると
「ブラウザ自体が画面の変更に対して行っていた処理を、JavaScriptで肩代わりすることで高速にレンダリングする技術」
です。
ブラウザは画面の構成要素をDOMという単位で管理しますが、これをJavaScriptで「仮想のオブジェクト」として管理することで ブラウザ内で行われているDOMを直接操作することをせず、モデルとビューのデータバインディングを行うことができます。
仮想DOMを採用しているライブラリ
有名なものとして以下が挙げられます。
React.js(Facebook社製ライブラリ https://reactjs.org/)
Vue.js(Google社のEvan Youさん個人が開発したライブラリ https://jp.vuejs.org/index.html)
今回の開発で採用するにあたって
ここで一旦SHOWROOM自体の機能について一度ご説明すると、大きく分けて以下の2つで成り立っています。
- 配信を視聴するための画面(様々な画面要素が存在し、通信が頻繁に発生する)
- それ以外の画面(簡単な設定変更のみを行うページや検索や記事的なページ)
今回新規で開発する画面は、後者であったため、複雑な処理は行う必要がありませんでした(実際に開発した画面の詳細は後述)。
そのため以下の点から、上記2つのライブラリは今回の開発には利用しないことにしました。
要件を満たすにはすこしオーバースペックである(機能は充実しているが、そこまで利用しない)
学習コストが高い(ライブラリ独自のお作法、周辺ライブラリの選定等、導入にあたって学習・検討しなくてはいけない内容が多い)
一度採用すると、そのライブラリを剥がしにくい(がっつりフレームワークに依存した処理が多ければ多いほど、書き換えコストも増大)
Hyperappについて
Hyperappは、JorgeBucaran(https://github.com/jorgebucaran)さんが開発している仮想DOMを実現しているライブラリです。
https://github.com/hyperapp/hyperapp
昨年末にIncrements社が運営するプログラマーコミュニティ「Qiita」において、ReactからHyperappに置き換えたことで話題になりました。 JorgeBucaranさんは、Qiitaの中のひと(2018年現在)で、独自に開発していたHyperappをオープン化しています。
今回、Hyperappを採用したのは以下の理由からです。
- ライブラリ自体がとても軽量
- 学習コストが低い
- ビューの管理にJSXをサポートしている
ライブラリ自体がとても軽量
ライブラリは1ファイルのみ、行数も400行未満、とてもシンプルな実装で軽量です。 自分で1からコードを読むのも全く苦ではないレベル、仮想DOMの考え方や実装を学ぶにもとてもよいライブラリだと思います。
学習コストが低い
先に述べた通りReactやVueは、学習・検討しなくてはいけない内容が多いのですが、Hyperappはこのライブラリ固有の考え方がとても少ないため導入が容易で、プロジェクトにあとから参加するメンバーがいても学習コストを低く抑えられます。
ビューの管理にJSXをサポートしている
同様にJSXをサポートしているReactに仮に置き換える必要がでたり、今後Reactで開発する必要がでてきて場合でも 書き換えが容易であり、モジュールの共有がしやすいと考えました。
(JSXについて:https://facebook.github.io/jsx/)
以上から、Hyperappを今回採用しました。 Hyperappの利点は、QiitaにJorgeBucaranさんが記事をあげていますのでそちらも参照するとさらにわかりやすいかと思います。
Hyperappのアーキテクチャ
Hyperappは以下の3つのモジュールで構成されています。
State
プレーンなJavaScriptオブジェクトであり、アプリケーション全体の状態を管理します。
Actions
Stateを更新するための処理群を管理します。このオブジェクト配下にある処理のみがStateを更新できます。
View
仮想DOMを実現します。ActionsによってStateが更新されるたびにViewが再描画される仕組みになっています。
以上がHyperapp独自の考え方のすべてなので、学習コストはとても低いといえます。
実際の開発について
ここからは、実際に開発した画面について、どんな構成にしたのか、どんなところでハマったのか、等を共有できればよいなと思っています。
(コストが低いのにハマったのかよ、というツッコミはまあ初期導入は得てしてそんなものだということでご理解ください。。。)
開発した画面
SHOWROOMは、ユーザの分身をアバターで表現しています。 仮想ライブ空間では、配信画面上にアバターがたくさん表示されており、いわゆる「ライブ感」を演出しています。
ユーザはアバターを頻繁に着替える機会があるのですが、着替えるための画面が使いにくいという声がユーザさんから多数上がっており、 この画面をより使いやすいように改修することになりました。
機能は以下の通りです。
- アバターのリストをページ送りで確認できるようにした。
- 条件で絞り込みできるようにした(使用した履歴 /「お気に入り」)。
- アバターを「お気に入り」設定し、管理できるようにした。
画面イメージ:PC
画面イメージ:アプリ
実際の画面はログインが必要です。
(PCの場合)
https://www.showroom-live.com/
左メニューより、ログイン -> マイプロフィールアイコン -> アバター着替えはこちらバナーをクリック
(アプリの場合)
「SHOWROOM-ライブ配信ならショールーム」をApp Storeで
左メニューより、ログイン -> マイページ -> 編集 -> アバター着替えはこちらバナーをクリック
または、
配信画面で画面下自分のアバターをクリック -> アバター着替えはこちらバナーをクリック
ぜひ使ってみてください!
開発環境
以下のような構成にしました。
JavaScriptライブラリ
Hyperapp 1x(アプリケーション全体の管理)
Hyperapp router(ルーティングの管理、今回はページ送りで使用)
axios(非同期処理)
開発言語
- TypeScript(https://www.typescriptlang.org/)
Flowでもよいのですが、現状TSをサポートしているライブラリのほうが多いのでこちらを採用。
- JSX(TSX)
スタイルライブラリ
- Bootstrap 4(https://getbootstrap.com/)
Showroom独自のコンポネントとテーマを適用しています。
タスクランナー
- webpack 4(https://webpack.js.org/)
(既存の画面もwebpack 4を使っています)
ディレクトリ構成
. ├── dev │ └── pages (webpack-dev-serverのrouting) ├── src │ ├── api (非同期処理まわり) │ ├── components (共通コンポーネント) │ ├── ext (外部ライブラリ) │ ├── icon (SVGアイコン用tsx) │ ├── img (静的画像) │ ├── pages (ページ固有の処理) │ │ └── UserAvatar │ │ ├── actions │ │ ├── components │ │ ├── icon │ │ ├── img │ │ ├── index.tsx │ │ ├── state │ │ └── utils │ ├── typings (TypeScript型定義ファイル) │ └── utils ├── tsconfig.json └── webpack.config.js
開発する上でのルール
以下のようなルールを最低限のコーディング規約としました。
- Actionsの命名規則
とくにHyperappによらない話で、Fluxを使った場合でも決めたほうが複数人の開発がスムーズになります。
今回Actionsの命名規則を、
「誰が」「どこを」「何をしたか」
で定義するようにしています。 具体例は以下のような感じです。
// ユーザ操作 // ユーザが、アバター画像を、クリックした userAvatarImageClicked: (id: number) => (state: State) => ActionResult<State> // システム // システムが、Appコンポーネントを、起動した systemAppLaunched: () => (state: State, actions: Actions) => ActionResult<State>
ハマった点
以下のようなところでハマりました。。。
非同期処理まわり
「ActionsによってStateが更新されるたびにViewが再描画される仕組み」というアーキテクチャにおいて、ActionsがStateを更新し反映するタイミングは、「Actionsで定義される関数の戻り値がStateを返却したとき」です。
公式にちゃんと書いてはあるのですが、横着して読み飛ばすとだめな典型。。。
https://github.com/hyperapp/hyperapp#asynchronous-actions
ルーティングまわり
後々簡単なSPAもできるように検証も兼ねて導入したHyperapp routerは、ライブラリが絶賛更新されているタイミングだったため、使い方がよくわからなかったり、TS型定義ファイルが公式になかったりしてなかなかビルドが通らないという羽目に。。。
また、ie11をサポートするために、polyfillを使用しています。 (Hyperapp routerでは内部で、CustomEventが使われています。)
TypeScriptまわり
いきなりTSまで導入するのがそもそもいけない説あります。。。 が、せっかくなので新規環境で早く導入したほうが、途中で導入するより楽です。
ハマりポイントはやはり型定義ファイルが見つからなかったり、サーバのテンプレートから渡されるデータとのやりとり、ネイティブオブジェクトをどうするか、等です。
今回すべてのファイルでTSを導入したのですが、そもそも「型の恩恵を得たいファイルのみに限定する」という割り切りが必要だと思います。
開発コミュニテイ
Hyperappは、公式のSlackコミュニティが活発なので、ハマった場合思い切ってそこで質問をなげてみるのも手です。
https://hyperappjs.herokuapp.com/
JorgeBucaranさんが、頻繁に発信したりするので最新の情報はこちらを見ていると追えます。 基本は英語のコミュニティですが、日本語のチャンネルも用意してあって、JorgeBucaranさんが日本語で答えてくれたりします。
その節はありがとうございました!mm
まとめ
まだあまり日本でのHyperappプロダクト導入事例は見たことがないため、チャレンジの意味も込めて導入してみましたが、一度導入してしまえばかなり楽に開発していける印象を持ちました。
ライブラリがかなりシンプルなため、もしハマったとしても解決のために内部を読むのはかなり楽にできそうです。
また、コミュニティが活発なライブラリは、更新速度も速いですが、より良い機能追加やサポートも受けやすいメリットもあります。
少し前の技術書典4でついに書籍がでていました。
https://booth.pm/ja/items/825889
この記事で、Hyperappに少しでも興味を持たれた方がいれば、ぜひ試しに導入してみてください!
小規模、中規模の機能改修にはかなり向いているのではないかと思います。
いっしょに働きませんか!
SHOWROOMでは、一緒にプロダクトを作り上げていく仲間を絶賛大募集中です。
自分たちが楽しく働ける仕組み、ユーザさんに良いと思ってもらえる機能をボトムアップでどんどん作り上げていける会社ですが、まだまだ人が足りません!
今回のようなチャレンジも、許容される組織です。
興味が湧いた方は一度ぜひ遊びに来てください! www.wantedly.com