この記事は一休.comアドベントカレンダー2018の13日目の記事です。
こんにちは。
今年の7月に入社したレストラン事業部の渥美です。
一休.com レストランにてフロントエンドとバックエンドの開発を行なっております。
この記事の概要
- 店舗ページをSPA化した背景
- 店舗ページリニューアル
- プラン詳細ページのSPA化
- Vue.js によるモーダルの実装方針
- 事前ロード
- モーダルの開閉
- URLを動的に生成する
- Fastly での段階的リリース
- Fastly について
- VCL の設定
- Fastly Fiddle によるテスト
- 今後の展望
店舗ページをSPA化した背景
店舗ページリニューアル
私が一休に入社する1ヶ月前、店舗の情報は複数のページにまたがっていました。
具体的には、店舗のページはプラン一覧や店舗情報、アクセスマップなどの情報が別々のタブに分かれており、ページ遷移が必要でした。
しかし、restaurant2*1へのリニューアルと共に、SPAとして生まれ変わったのでした。


プラン詳細ページのSPA化
その後入社した私はユーザ体験をさらに向上させるために、 独立していたプランの詳細ページのモーダル化を担当しました。 これにより、店舗ページ内で完結できる範囲が広がりより予約がしやすいUIになりました。
※現在も下記のプラン詳細ページは動いていますが、後述するようにモーダルへ移行予定です。


今回はこのモーダル化の実装について簡単にお話するとともに、
段階的リリースの取り組みについてご紹介します。
Vue.js によるモーダルの実装方針
一休レストランのフロントエンド開発では Vue.js が使われています。 モーダルの実装もVue.jsで制御しており、今回はその基本的な実装方針を紹介します。
大まかな処理は以下のとおりです。
// 店舗ページの Vue ファイル <template> <!-- 略 --> <object-plan-item @mouseenter="preloadPlanDetail" @click:plan="openPlanDetail" /> <composition-plan-detail-modal :activated="showPlanDetail" @update:activated="onClose" /> </template> export default { // 略 data() { return { showPlanDetail: false }; }, methods: { openPlanDetail(plan) { // ①モーダルの開閉処理 this.showPlanDetail = true; // ②URL の動的生成 global.history.pushState(null, '', this.planDetailUrl(plan)); }, onClose() { // ①モーダルの開閉処理 this.showPlanDetail = false; // ②URL の動的生成 global.history.pushState(null, '', `/${this.restaurant.id}/`); }, preloadPlanDetail(plan) { // ③プラン詳細情報の先読み }, planDetailUrl(plan) { // URL を生成し返却する }, } }
<object-plan-item>
が各プラン項目のコンポーネント、
<composition-plan-detail-modal>
がモーダルのコンポーネントです。
①モーダルの開閉処理
モーダルの表示状態は showPlanDetail
を定義して、この boolean 値で管理しています。
単純ですが、プランの「詳細・予約」ボタンがクリックされたときに showPlanDetail = true
,
モーダルの背景がクリックされたときに showPlanDetail = false
となり、表示が切り替わります。
②URL の動的生成
モーダルを開いたときに URL を動的に生成しています。
History API を利用して、
global.history.pushState(null, '', this.planDetailUrl({ plan, time }));
とすることでモーダルを開いたときに URL が更新され、履歴にも追加するようにしています。
モーダルを閉じるときは、
global.history.pushState(null, '', `/${this.restaurant.id}/`);
として店舗ページの URL に戻します。
③プランの詳細情報を先読み
プランの詳細情報を先に読み込む処理を書いています。
実際には <object-plan-item>
から mouseenter
のイベントが emit され、preloadPlanDetail
が発火されます。
ユーザからすると、プラン一覧の項目をマウスオーバーすると同時にプリロードが走るので
モーダルを開いたときにはシームレスにプラン詳細が表示される体験を提供できます。
(※ローディングが完了していない場合はローディング画像が表示されます)
勿論他にも処理はありますが、以上がモーダル化の大まかな実装となります。
Vue.js を使うことでモーダルの開閉状態や、先読みしたプランの情報をモーダルに受け渡せるのは便利な点でした。
Fastly での段階的リリース
今回モーダル化したページは予約導線に直結しているということもあり、 リリース当初は特定の店舗のみに限定公開しました。 この限定公開に Fastly を使いました。
Fastly について
一休ではリバースプロキシとして Fastly を利用しています。 Fastly は設定言語の VCL を書くことでリダイレクトの処理や、レスポンスの制御を簡単に行うことができます。
今回は、特定の店舗ページにアクセスしたときのみ、?modal_enabled=1
というクエリパラメータを付与するように VCLの設定を行いました。
そして、このパラメータが付与されている場合はモーダルを開くようにアプリケーション側でハンドリングしました。
流れとしては下記のようなイメージです。
- 特定店舗のURLにアクセスする
- Fastly で
?modal_enabled=1
が付与されたURLにリダイレクトさせる- https://restaurant.ikyu.com/(店舗ID)?modal_enabled=1
?modal_enabled=1
が存在するときのみアプリケーション側でモーダルを開く
メリット
Fastly での段階的リリースのメリットとしては下記のようなものがあります。
- 限定リリースができる
- 今回の主目的である、URLに応じた限定リリースが可能です。
- Fastly のデプロイが高速
- VCL の本番環境への反映が、CIのテストを含めて2分ほどで完了します。
- 切り戻しが容易
- 上記のデプロイが高速であることと関係するのですが、なにかトラブルがあった場合の切り戻しが容易です。 アプリケーションのリリースは20分程かかるのでこれはありがたいポイントです。
それまで他の手法での段階的リリースは行われていたものの、
Fastly を使った段階的リリースは初めての試みでした。
VCL の設定
さて、実際に VCL で設定した内容をご紹介します。(必要に応じて変数名等を変えています)
table test_ids { "100000": "true", } sub vcl_recv { #FASTLY recv if (req.url.qs !~ "modal_enabled=" && req.url.path ~ "^\/(\d{6})" && table.lookup(test_ids, re.group.1) ) { error 700; # 内部的にerror statusを飛ばすことでリダイレクトを行う } } sub vcl_error { #FASTLY error if (obj.status == 700) { set obj.http.Location = "https://" req.http.host req.url.path "?modal_enabled=1" if(req.url.qs == "", "", "&" req.url.qs); set obj.status = 307; set obj.response = "Temporary Redirect"; return (deliver); } }
table
の項目で特定店舗のIDを指定しています。
ここで気になった方もいるかも知れませんが、 VCL の設定では内部的に error status を飛ばすことでリダイレクトを行います。
Fastly Fiddle によるテスト
この VCL の設定を行うにあたり、Fastly Fiddle というサービスを利用しました。これは VCL のコードをオンラインで簡単にテストできるサービスです。 Fastly Labsが実験的に運用しているようです。

Fastly Fiddle は実行したコードを他のユーザと共有することもできます。
上記のコードをサンプルとして用意しました。
ページにアクセスしたら、右上の[RUN]ボタンを押してみてください。
table test_ids
に含まれるIDに対応するURL(/100000)に対してリクエストを送ると、リダイレクトが発生することがわかります。

反対に、test_ids
に含まれないIDに対応するURL(例えば /200000)にリクエストを送ってもリダイレクトされません。
是非Send a request
の項目を/100000
から変更して遊んでみてください。
このようにして、Fastly による特定店舗のみの段階的リリースができるようになりました。
今後の展望
今回のモーダル実装で店舗のページのSPA化が進みました。
しかし、モーダルのURLに直接アクセスしたときは、未だ以前のプラン詳細ページに遷移するようになっています。
今後はこのような導線にもモーダルが開くように対応していきます。
また、今回 Fastly での段階的リリースの仕組みも整ったので、今後の機能追加でどんどん活用していきたいと思います。
明日は id:kentana20 さんの 12/12 Ikyu Frontend Meetup 開催レポート です! お楽しみに!
*1:新しいアーキテクチャによる一休.com レストランのWebアプリケーションを指す。