Mainline Menswear の事例: PWA を導入した結果、コンバージョン率が 55% アップ

Mainline はファッション関連の大手デザイナー ブランドを販売するオンラインの衣料品小売業者です。英国を拠点とする同社は、すべての顧客にスムーズなショッピング エクスペリエンスを提供できるよう、主要パートナーと戦略的に融合された社内の専門家チームに依頼しています。Mainline は 7 つの地域用に開発された 7 つのウェブサイトとアプリを通じて 100 か国以上の市場で存在感を示しており、今後も e コマース サービスが競合他社に対抗し続けることでしょう。

課題

Mainline Menswear の目標は、成長するスマートフォン市場を考慮したモバイル フレンドリーなデザインと機能に重点を置き、同社の「モバイル ファースト」のビジョンに沿った先進的な機能で、現在のモバイル向けに最適化されたウェブサイトを補完することでした。

解決策

目標は、Mainline Menswear ウェブサイトの元のモバイル フレンドリー バージョンを補完する PWA を構築してリリースし、統計情報を、現在 Android と iOS で利用可能なハイブリッド モバイルアプリと比較することでした。

アプリをリリースして Mainline Menswear ユーザーのごく一部によって使用されていたことで、PWA、アプリ、ウェブの主要な統計情報の違いを特定することができました。

Mainline がウェブサイトを PWA に変換する際に採用したアプローチは、ウェブサイト用に選択したフレームワーク(Nuxt.js、Vue.js を使用)が将来にわたって有効であることを確認し、動きの速いウェブ テクノロジーを活用できるようにすることでした。

結果

139%

ウェブと比べて PWA ではセッションあたりのページビュー数が増加

161%

ウェブより PWA の方がセッション継続時間が長い。

10%

PWA の直帰率の低下はウェブとの比較

12.5%

PWA での平均注文額の伸び(ウェブとの比較)

55%

PWA のコンバージョン率はウェブより向上。

243%

ウェブと比べて PWA ではセッションあたりの収益が増加

技術的な詳細

Mainline Menswear は、Nuxt.js フレームワークを使用して、シングルページ アプリケーション(SPA)であるサイトをバンドルしてレンダリングしています。

Service Worker ファイルの生成

Service Worker を生成するために、Mainline Menswear は nuxt/pwa Workbox モジュールのカスタム実装を通じて構成を追加しました。

nuxt/pwa モジュールをフォークした理由は、標準バージョンではできなかった、または問題があった Service Worker ファイルに、さらにカスタマイズを追加できるようにするためです。そうした最適化の 1 つとして、サイトのオフライン機能(デフォルトのオフライン ページの提供や、オフラインでの分析の収集など)に関するものがありました。

ウェブアプリ マニフェストの構造

チームは、さまざまなモバイルアプリのアイコンサイズと、namedescriptiontheme_color などのウェブアプリの詳細について、アイコンを含むマニフェストを生成しました。

{
  "name": "Mainline Menswear",
  "short_name": "MMW",
  "description": "Shop mens designer clothes with Mainline Menswear. Famous brands including Hugo Boss, Adidas, and Emporio Armani.",
  "icons": [
    {
      "src": "/_nuxt/icons/icon_512.c2336e.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "theme_color": "#107cbb"
}

ウェブアプリをインストールすると、ブラウザを介さずにホーム画面から起動できます。そのためには、ウェブアプリのマニフェスト ファイルに display パラメータを追加します。

{
  "display": "standalone"
}

さらに、マニフェストの start_url フィールドに utm_source パラメータを追加するだけで、ホーム画面からウェブアプリにアクセスしているユーザーの数を簡単に追跡できるようになりました。

{
  "start_url": "/?utm_source=pwa"
}

ナビゲーションを高速化するランタイム キャッシュ

ウェブアプリのキャッシュ保存は、ページ速度を最適化し、リピーターのユーザー エクスペリエンスを向上させるために不可欠です。

ウェブ上でのキャッシュ保存には、非常に多くのさまざまなアプローチがあります。チームは、クライアント側でアセットをキャッシュに保存するために、HTTP キャッシュCache API を組み合わせて使用しています。

Cache API を使用すると、Mainline Menswear はキャッシュされたアセットをきめ細かく制御し、各ファイル形式に複雑な戦略を適用できます。どれも複雑で、設定と保守が難しいように思えますが、Workbox を使用すれば、このような複雑な戦略を簡単に宣言でき、メンテナンスの手間を軽減できます。

CSS と JS のキャッシュ保存

CSS ファイルと JS ファイルについては、StaleWhileRevalidate ワークボックス戦略を使用してキャッシュに保存し、キャッシュ経由で提供することにしました。この戦略により、すべての Nuxt CSS および JS ファイルを迅速に提供でき、サイトのパフォーマンスが大幅に向上します。同時に、ファイルが次回アクセスできるように、バックグラウンドで最新バージョンに更新されます。

/* sw.js */
workbox.routing.registerRoute(
  /\/_nuxt\/.*(?:js|css)$/,
  new workbox.strategies.StaleWhileRevalidate({
    cacheName: 'css_js',
  }),
  'GET',
);

Google Fonts のキャッシュ保存

Google Fonts をキャッシュに保存する戦略は、次の 2 つのファイル形式によって異なります。

  • @font-face 宣言を含むスタイルシート。
  • 基になるフォント ファイル(上記のスタイルシートでリクエスト)。
// Cache the Google Fonts stylesheets with a stale-while-revalidate strategy.
workbox.routing.registerRoute(
  /https:\/\/fonts\.googleapis\.com\/*/,
  new workbox.strategies.StaleWhileRevalidate({
    cacheName: 'google_fonts_stylesheets',
  }),
  'GET',
);

// Cache the underlying font files with a cache-first strategy for 1 year.
workbox.routing.registerRoute(
  /https:\/\/fonts\.gstatic\.com\/*/,
  new workbox.strategies.CacheFirst({
    cacheName: 'google_fonts_webfonts',
    plugins: [
      new workbox.cacheableResponse.CacheableResponsePlugin({
        statuses: [0, 200],
      }),
      new workbox.expiration.ExpirationPlugin({
        maxAgeSeconds: 60 * 60 * 24 * 365, // 1 year
        maxEntries: 30,
      }),
    ],
  }),
  'GET',
);

画像のキャッシュ保存

画像については、2 つの戦略を採用することにしました。1 つ目の戦略は、CDN からのすべての画像(通常は商品画像)に適用されます。このページは画像が多いため、デバイスのストレージを使いすぎないように意識しています。そこで同社は Workbox を通じて、ExpirationPlugin を使用して CDN からのみ取得された画像を最大 60 枚でキャッシュに保存する戦略を追加しました。

リクエストされた 61 番目の(最も新しい)画像で最初の(古い)画像が置き換えられるため、任意の時点でキャッシュに保存される商品画像は 60 個以下になります。

workbox.routing.registerRoute(
  ({ url, request }) =>
    url.origin === 'https://mainline-menswear-res.cloudinary.com' &&
    request.destination === 'image',
  new workbox.strategies.StaleWhileRevalidate({
    cacheName: 'product_images',
    plugins: [
      new workbox.expiration.ExpirationPlugin({
        // Only cache 60 images.
        maxEntries: 60,
        purgeOnQuotaError: true,
      }),
    ],
  }),
);

2 つ目の画像戦略では、送信元からリクエストされた残りの画像を処理します。これらの画像は、オリジン全体で非常に少ない傾向がありますが、念のため、キャッシュに保存される画像の数も 60 に制限されています。

workbox.routing.registerRoute(
  /\.(?:png|gif|jpg|jpeg|svg|webp)$/,
  new workbox.strategies.StaleWhileRevalidate({
    cacheName: 'images',
    plugins: [
      new workbox.expiration.ExpirationPlugin({
        // Only cache 60 images.
        maxEntries: 60,
        purgeOnQuotaError: true,
      }),
    ],
  }),
);

オフライン機能の提供

オフライン ページは、Service Worker がインストールされてアクティベートされた直後に事前キャッシュされます。そのために、すべてのオフライン依存関係のリスト(オフライン HTML ファイルとオフライン SVG アイコン)を作成します。

const OFFLINE_HTML = '/offline/offline.html';
const PRECACHE = [
  { url: OFFLINE_HTML, revision: '70f044fda3e9647a98f084763ae2c32a' },
  { url: '/offline/offline.svg', revision: 'efe016c546d7ba9f20aefc0afa9fc74a' },
];

その後、事前キャッシュ リストは Workbox にフィードされます。Workbox は、キャッシュへの URL の追加、リビジョンの不一致の確認、事前キャッシュされたファイルの更新と CacheFirst 戦略による提供といった面倒な作業をすべて行います。

workbox.precaching.precacheAndRoute(PRECACHE);

オフライン ナビゲーションの処理

Service Worker が有効になり、オフライン ページが事前キャッシュされると、それを使用してユーザーによるオフライン ナビゲーション リクエストに応答します。Mainline Menswear のウェブアプリは SPA ですが、オフライン ページは、ページが再読み込みされた後、ユーザーがブラウザタブを閉じて再度開いたとき、またはオフライン中にホーム画面からウェブアプリが起動されたときにのみ表示されます。

これを実現するために、Mainline Menswear は、事前キャッシュされたオフライン ページを使用して、失敗した NavigationRoute リクエストへのフォールバックを提供しています。

const htmlHandler = new workbox.strategies.NetworkOnly();
const navigationRoute = new workbox.routing.NavigationRoute(({ event }) => {
    const request = event.request;
    // A NavigationRoute matches navigation requests in the browser, i.e. requests for HTML
    return htmlHandler.handle({ event, request }).catch(() => caches.match(OFFLINE_HTML, {
        ignoreSearch: true
    }));
});
workbox.routing.registerRoute(navigationRoute);

デモ

www.mainlinemenswear.co.uk に表示されたオフライン ページの例

成功したインストールの報告

ウェブアプリは、ホーム画面の起動トラッキング(ウェブ アプリケーション マニフェストの "start_url": "/?utm_source=pwa" を使用)のほかに、windowappinstalled イベントをリッスンすることで、アプリのインストール成功を報告します。

window.addEventListener('appinstalled', (evt) => {
  ga('send', 'event', 'Install', 'Success');
});

ウェブサイトに PWA 機能を追加すると、一緒にショッピングを楽しむユーザー エクスペリエンスがさらに向上し、[プラットフォーム固有] のアプリよりも市場投入までの時間を短縮できます。

開発責任者、Andy Hoyle

おわりに

プログレッシブ ウェブアプリとその作成方法について詳しくは、web.dev のプログレッシブ ウェブアプリのセクションをご覧ください。

プログレッシブ ウェブアプリのその他の事例紹介については、事例紹介のセクションをご覧ください。