Mainline 男装实施了 PWA,转化率提高了 55%

Mainline 是一家线上服装零售商,经营大牌时装设计师品牌。这家英国公司委托其内部专家团队与关键合作伙伴进行战略性整合,以便为所有用户提供顺畅的购物体验。Mainline 通过 7 个定制的区域性网站和一个应用,在 100 多个国家/地区开展市场推广,将继续确保电商服务能够与竞争对手相媲美。

挑战

Mainline Menswear 的目标是为当前针对移动设备进行了优化的网站补充符合其“移动优先”愿景的渐进式功能,专注于适合移动设备的设计和功能,满足不断增长的智能手机市场需求。

解决方案

目标是构建并发布一款 PWA,对 Mainline 男装网站的原始移动设备适用版本进行补充,然后将统计信息与其混合移动应用(目前适用于 Android 和 iOS 应用)进行比较。

当应用启动并被 Mainline 男装一小部分用户使用后,他们就能够确定 PWA、应用和 Web 之间的关键统计信息之间的差异。

在将其网站转换为 PWA 时,Mainline 采取的方法是确保其为网站选择的框架(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 文件添加更多在使用标准版本时无法添加或出现问题的自定义内容。其中一项优化涉及网站的离线功能,例如提供默认离线网页和在离线状态下收集分析数据。

Web 应用清单详解

该团队生成了一个清单,其中包含用于不同移动应用图标大小的图标以及其他 Web 应用详细信息(如 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"
}

Web 应用安装后,可从主屏幕启动,而无需浏览器进行干预。这可通过在 Web 应用清单文件中添加 display 参数来实现:

{
  "display": "standalone"
}

最后但同样重要的一点是,该公司现在只需在清单的 start_url 字段中附加一个 utm_source 参数,就能轻松跟踪有多少用户从主屏幕访问他们的 Web 应用:

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

可利用运行时缓存加快导航速度

为优化网页速度和为回访用户提供更出色的用户体验,必须对 Web 应用进行缓存。

针对 Web 缓存,有多种不同的方法。该团队混合使用 HTTP 缓存Cache API 在客户端缓存资源。

借助 Cache API, Mainline Menswear 可以更好地控制缓存的资源,从而能够对每种文件类型应用复杂的策略。虽然所有这些听起来都很复杂且难以设置和维护,但 Workbox 为他们提供了一种简单的方式来声明此类复杂的策略,并减轻了维护工作的麻烦。

缓存 CSS 和 JS

对于 CSS 和 JS 文件,该团队选择对其进行缓存,并使用 StaleWhileRevalidate Workbox 策略通过缓存传送它们。此策略使他们能够快速提供所有 Nuxt CSS 和 JS 文件,从而显著提高其网站的性能。同时,这些文件会在后台更新为最新版本,以供下次访问:

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

缓存 Google 字体

缓存 Google Fonts 的策略取决于两种文件类型:

  • 包含 @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',
);

缓存图片

在图片方面,Mainline Menswear 决定采用两种策略。第一种策略适用于来自 CDN 的所有图片,通常为商品图片。他们的页面包含大量图片,因此有意识地避免了占用用户过多的设备存储空间。因此,通过 Workbox,他们添加了一项策略,即使用 ExpirationPlugin 仅缓存来自 CDN 的图片最多缓存 60 张图片

请求的第 61 张(最新)图片会替换第 1 张(最旧)图片,以便在任何时间点缓存的商品图片都不超过 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,
      }),
    ],
  }),
);

第二种图片策略会处理源站所请求的其余图片。在整个来源中,这些图片往往非常少且非常小,但为了安全起见,这些缓存图片的数量也限制为 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 将处理将网址添加到缓存、检查任何修订版本不匹配、更新以及使用 CacheFirst 策略提供预缓存文件的所有繁重工作。

workbox.precaching.precacheAndRoute(PRECACHE);

处理离线导航

在 Service Worker 激活并预缓存离线页面后,它将用于响应用户的离线导航请求。虽然 Mainline Menswear 的 Web 应用是 SPA,但离线页面仅在页面重新加载、用户关闭并重新打开浏览器标签页后显示,或者在离线状态下从主屏幕启动 Web 应用时显示。

为了实现这一点,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 上的离线页面示例。

报告成功安装

除了主屏幕启动跟踪(在 Web 应用清单中使用 "start_url": "/?utm_source=pwa")之外,Web 应用还会通过监听 window 上的 appinstalled 事件来报告应用安装成功:

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

向您的网站添加 PWA 功能可进一步提升客户的购物体验,并且会比 [平台专用] 应用更快上市。

Andy Hoyle,开发主管

总结

如需详细了解渐进式 Web 应用以及如何构建这些应用,请访问 web.dev 上的“渐进式 Web 应用”部分

要详细了解渐进式 Web 应用案例研究,请浏览“案例研究”部分