Mainline Menswear ที่ใช้ PWA และมีอัตรา Conversion เพิ่มขึ้น 55%

Mainline เป็นผู้ค้าปลีกเสื้อผ้าออนไลน์ที่นำเสนอแบรนด์ดีไซเนอร์ชื่อดังที่สุดในวงการแฟชั่น บริษัทในสหราชอาณาจักรมอบความไว้วางใจให้ทีมผู้เชี่ยวชาญภายในที่ผสมผสานอย่างมีกลยุทธ์กับพาร์ทเนอร์รายสำคัญเพื่อมอบประสบการณ์การช็อปปิ้งที่ราบรื่นสำหรับทุกคน Mainline ให้บริการในกว่า 100 ประเทศผ่านเว็บไซต์และแอปสำหรับเขตแดนที่สร้างขึ้นเอง 7 แห่ง Mainline จะยังคงดูแลให้ข้อเสนออีคอมเมิร์ซสามารถแข่งขันกับคู่แข่งได้

ความท้าทาย

เป้าหมายของ Mainline Menswear คือการเสริมเว็บไซต์ที่เพิ่มประสิทธิภาพสำหรับอุปกรณ์เคลื่อนที่ในปัจจุบันด้วยฟีเจอร์ที่ก้าวหน้าซึ่งสอดคล้องกับวิสัยทัศน์ของ "อุปกรณ์เคลื่อนที่เป็นอันดับแรก" โดยมุ่งเน้นการออกแบบและฟังก์ชันที่เหมาะกับอุปกรณ์เคลื่อนที่โดยคำนึงถึงตลาดสมาร์ทโฟนที่กำลังเติบโตเป็นหลัก

โซลูชัน

วัตถุประสงค์คือการสร้างและเปิดตัว PWA ที่มาเสริมเว็บไซต์ Mainline Menswear เวอร์ชันเดิมที่เหมาะกับอุปกรณ์เคลื่อนที่ แล้วเปรียบเทียบสถิติกับแอปบนอุปกรณ์เคลื่อนที่แบบไฮบริดซึ่งปัจจุบันพร้อมให้บริการบน Android และ iOS

เมื่อเปิดตัวแอปและมีผู้ใช้ส่วนบุรุษ Mainline เพียงส่วนน้อยใช้แอปดังกล่าว ทางทีมสามารถระบุความแตกต่างของสถิติสำคัญระหว่าง PWA, แอป และเว็บได้

แนวทางที่ Mainline ใช้เมื่อแปลงเว็บไซต์เป็น PWA คือการตรวจสอบว่าเฟรมเวิร์กซึ่งเลือกไว้สําหรับเว็บไซต์ (Nuxt.js ที่ใช้ Vue.js) จะใช้งานได้ในอนาคตและช่วยให้ใช้ประโยชน์จากเทคโนโลยีเว็บที่พัฒนาอย่างรวดเร็วได้

ผลลัพธ์

139%

จำนวนหน้าเว็บต่อเซสชันใน PWA มากกว่าในเว็บ

161%

ระยะเวลาเซสชันที่นานขึ้นใน PWA เทียบกับเว็บ

10%

อัตราตีกลับใน PWA ที่ต่ำกว่าเมื่อเทียบกับเว็บ

12.5%

มูลค่าการสั่งซื้อเฉลี่ยสูงกว่าใน PWA เมื่อเทียบกับเว็บ

55%

อัตรา Conversion ใน PWA สูงกว่าในเว็บ

243%

รายได้ต่อเซสชันใน PWA สูงกว่าในเว็บ

ข้อมูลทางเทคนิคแบบเจาะลึก

Mainline Menswear ใช้เฟรมเวิร์ก Nuxt.js เพื่อรวมและแสดงผลเว็บไซต์ ซึ่งเป็นแอปพลิเคชันหน้าเว็บเดียว (SPA)

การสร้างไฟล์ Service Worker

ส่วนการสร้าง Service Worker นั้น Mainline Menswear ได้เพิ่มการกำหนดค่าผ่านการใช้งานแบบกำหนดเองของโมดูล nuxt/pwa Workbox

เหตุผลที่แยกโมดูล nuxt/pwa ออกมาคือเพื่อให้ทีมเพิ่มการปรับแต่งเพิ่มเติมในไฟล์ Service Worker ที่ไม่สามารถทำได้หรือพบปัญหาเมื่อใช้เวอร์ชันมาตรฐาน การเพิ่มประสิทธิภาพอย่างหนึ่งคือฟังก์ชันการทำงานแบบออฟไลน์ของเว็บไซต์ เช่น การแสดงหน้าเว็บออฟไลน์เริ่มต้นและการรวบรวมข้อมูลวิเคราะห์ขณะออฟไลน์

โครงสร้างของไฟล์ Manifest ของเว็บแอป

ทีมสร้างไฟล์ Manifest ที่มีไอคอนสำหรับขนาดไอคอนแอปบนอุปกรณ์เคลื่อนที่ขนาดต่างๆ และรายละเอียดอื่นๆ ของเว็บแอป เช่น name, description และ theme_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 ในไฟล์ Manifest ของเว็บแอป

{
  "display": "standalone"
}

สุดท้ายนี้ บริษัทสามารถติดตามจํานวนผู้ใช้ที่เข้าชมเว็บแอปจากหน้าจอหลักได้โดยง่าย เพียงเพิ่มพารามิเตอร์ utm_source ในช่อง start_url ของไฟล์ Manifest ดังนี้

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

การแคชรันไทม์เพื่อการไปยังส่วนต่างๆ ที่รวดเร็วยิ่งขึ้น

การแคชสําหรับเว็บแอปเป็นสิ่งที่จําเป็นต่อการเพิ่มประสิทธิภาพความเร็วหน้าเว็บและมอบประสบการณ์การใช้งานที่ดีขึ้นให้แก่ผู้ใช้ที่กลับมา

สำหรับการแคชบนเว็บ มีวิธีการที่แตกต่างกันอยู่ 2-3 วิธี ทีมดังกล่าวใช้แคช HTTP และ Cache API ร่วมกันสำหรับการแคชเนื้อหาในฝั่งไคลเอ็นต์

Cache API ช่วยให้ Mainline Menswear ควบคุมเนื้อหาที่แคชไว้ได้ละเอียดยิ่งขึ้น ซึ่งช่วยให้ใช้กลยุทธ์ที่ซับซ้อนกับไฟล์แต่ละประเภทได้ แม้ว่าฟังดูซับซ้อนและตั้งค่าและดูแลรักษาได้ยาก แต่ Workbox ช่วยให้ประกาศกลยุทธ์ที่ซับซ้อนดังกล่าวได้อย่างง่ายดายและลดความยุ่งยากในการดูแลรักษา

การแคช CSS และ JS

สําหรับไฟล์ CSS และ JS ทางทีมเลือกที่จะแคชไฟล์และแสดงผ่านแคชโดยใช้กลยุทธ์ Workbox ของ StaleWhileRevalidate กลยุทธ์นี้ช่วยให้แสดงไฟล์ CSS และ JS ทั้งหมดของ Nuxt ได้อย่างรวดเร็ว ซึ่งช่วยเพิ่มประสิทธิภาพของเว็บไซต์ได้อย่างมาก ในขณะเดียวกัน ระบบจะอัปเดตไฟล์ในเบื้องหลังเป็นเวอร์ชันล่าสุดสำหรับการเข้าชมครั้งถัดไป

/* 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',
);

การแคชรูปภาพ

สำหรับภาพนั้น Mainline Menswear ตัดสินใจเลือก 2 กลยุทธ์ด้วยกัน กลยุทธ์แรกใช้กับรูปภาพทั้งหมดที่มาจาก CDN ซึ่งมักจะเป็นรูปภาพผลิตภัณฑ์ หน้าเว็บมีรูปภาพจำนวนมาก จึงต้องคำนึงถึงการใช้พื้นที่เก็บข้อมูลในอุปกรณ์ของผู้ใช้ให้น้อยที่สุด ดังนั้น พวกเขาจึงเพิ่มกลยุทธ์แคชรูปภาพจาก CDN เท่านั้นโดยใช้ รูปภาพสูงสุด 60 รูปผ่าน Workbox โดยใช้ ExpirationPlugin

รูปภาพที่ 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,
      }),
    ],
  }),
);

กลยุทธ์รูปภาพลำดับที่ 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 ซึ่งทำได้โดยการสร้างรายการทรัพยากร Dependency แบบออฟไลน์ทั้งหมด ได้แก่ ไฟล์ HTML ออฟไลน์และไอคอน SVG แบบออฟไลน์

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

จากนั้นระบบจะส่งรายการที่แคชไว้ล่วงหน้าไปยัง Workbox ซึ่งจะจัดการงานหนักทั้งหมดในการเพิ่ม URL ลงในแคช ตรวจสอบการแก้ไขที่ไม่ตรงกัน อัปเดต และแสดงไฟล์ที่แคชไว้ล่วงหน้าด้วยกลยุทธ์ CacheFirst

workbox.precaching.precacheAndRoute(PRECACHE);

การจัดการการนําทางแบบออฟไลน์

เมื่อ Service Worker เปิดใช้งานและมีการแคชหน้าเว็บออฟไลน์ไว้ล่วงหน้า ระบบจะใช้ 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" ในไฟล์ Manifest ของแอปบนเว็บ) เว็บแอปยังรายงานการติดตั้งแอปที่สำเร็จโดยรับฟังเหตุการณ์ appinstalled ใน window ด้วย

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

การเพิ่มความสามารถของ PWA ลงในเว็บไซต์จะช่วยปรับปรุงประสบการณ์การช็อปปิ้งของลูกค้าให้ดียิ่งขึ้น และคุณจะทําการตลาดได้เร็วกว่าแอป [เฉพาะแพลตฟอร์ม]

Andy Hoyle หัวหน้าฝ่ายพัฒนา

บทสรุป

ดูข้อมูลเพิ่มเติมเกี่ยวกับ Progressive Web App และวิธีสร้างได้ที่ส่วน Progressive Web App ใน web.dev

หากต้องการอ่านกรณีศึกษาเพิ่มเติมเกี่ยวกับ Progressive Web App ให้ไปที่ส่วนกรณีศึกษา