Wake Lock API 案例研究:BettyCrocker.com 购买意向指标提升 300%

使用移动设备烹饪时,最糟糕的事情莫过于在食谱步骤进行到一半时屏幕关闭。了解烹饪网站 BettyCrocker.com 如何使用 Wake Lock API 来防止这种情况发生。

近一个世纪以来,Betty Crocker 一直是美国现代烹饪指导和值得信赖的食谱开发的来源。他们的网站 BettyCrocker.com 于 1997 年推出,如今每月访问量超过 1200 万。在他们实施 Wake Lock API 后,与所有用户相比,他们的 Wake Lock 用户购买意向指标高出约 300%

已停用的 iOS 和 Android 应用

Betty Crocker 的应用在 2014 年发布时备受瞩目,但最近由于优先级降低,已从 Apple App Store 和 Google Play Store 中下架。长期以来,Betty Crocker 团队一直倾向于向移动网站而不是 iOS/Android 应用添加新功能。创建 iOS/Android 应用的技术平台已过时,并且企业没有资源来支持更新和维护这些应用。Web 应用在流量方面也明显更大,更现代,并且更易于增强。

iOS/Android 应用确实有一个杀手级功能,深受用户喜爱

千禧一代烹饪专业提示:当您按照食谱操作时,@BettyCrocker 移动应用不会变暗或锁定。—@AvaBeilke

80% 的人在厨房使用设备烹饪,但屏幕变暗和锁定是一个问题。@BettyCrocker 做了什么?更新了他们的应用,以便在用户查看食谱时不会变暗。—@KatieTweedy

使用 Wake Lock API 将杀手级功能引入 Web

使用设备烹饪时,没有什么比在屏幕关闭时不得不触摸沾满污渍的手或什至是鼻子更令人沮丧的了。Betty Crocker 问自己,他们如何才能将 iOS/Android 应用的杀手级功能移植到 Web 应用上。这时,他们了解到了 Project FuguWake Lock API

A person kneading dough on a kitchen table covered in flour

Wake Lock API 提供了一种防止设备屏幕变暗或锁定的方法。此功能支持全新的体验,而这些体验在以前需要 iOS/Android 应用才能实现。Wake Lock API 减少了对 hacky 且可能耗电的变通方法的需求。

请求 Wake Lock

要请求 Wake Lock,您需要调用返回 WakeLockSentinel 对象的 navigator.wakeLock.request() 方法。您将使用此对象作为哨兵值。浏览器可以出于各种原因(例如,因为电池电量过低)拒绝请求,因此最好将调用包装在 try…catch 语句中。

释放 Wake Lock

您还需要一种释放 Wake Lock 的方法,这可以通过调用 WakeLockSentinel 对象的 release() 方法来实现。如果您希望在经过一定时间后自动释放 Wake Lock,则可以使用 window.setTimeout() 调用 release(),如下例所示。

// The wake lock sentinel.
let wakeLock = null;

// Function that attempts to request a wake lock.
const requestWakeLock = async () => {
  try {
    wakeLock = await navigator.wakeLock.request('screen');
    wakeLock.addEventListener('release', () => {
      console.log('Wake Lock was released');
    });
    console.log('Wake Lock is active');
  } catch (err) {
    console.error(`${err.name}, ${err.message}`);
  }
};

// Request a wake lock…
await requestWakeLock();
// …and release it again after 5s.
window.setTimeout(() => {
  wakeLock.release();
  wakeLock = null;
}, 5000);

实施

借助新的 Web 应用,用户应该能够轻松浏览食谱、完成步骤,甚至可以在不锁定屏幕的情况下走开。为了实现此目标,该团队首先构建了一个快速的前端原型作为概念验证并收集 UX 输入。

在原型证明有用之后,他们设计了一个 Vue.js 组件,该组件可以在他们的所有品牌(BettyCrockerPillsburyTablespoon)之间共享。即使只有 Betty Crocker 拥有 iOS 和 Android 应用,这三个站点也确实拥有共享的代码库,因此他们能够一次性实施该组件,并将其部署到所有地方,如下面的屏幕截图所示。

BettyCrocker.com wake lock toggle
BettyCrocker.com Wake Lock 开关。
Pillsbury.com wake lock toggle
Pillsbury.com Wake Lock 开关。
Tablespoon.com wake lock toggle
Tablespoon.com Wake Lock 开关。

在基于新站点的现代化框架开发组件时,重点放在了 MVVM 模式的 ViewModel。该团队在编程时还考虑了互操作性,以便在站点的旧框架和新框架上启用功能。

为了跟踪可见性和可用性,Betty Crocker 集成了 Wake Lock 生命周期中核心事件的分析跟踪。该团队利用功能管理将 Wake Lock 组件部署到单个站点以进行初始生产部署,然后在监控使用情况和页面健康状况后将该功能部署到其余站点。他们继续根据此组件的使用情况监控分析数据。

作为用户的后备方案,该团队创建了一个强制超时,在一小时不活动后禁用 Wake Lock。他们最终确定的最终实施方案是在其所有站点的所有食谱页面上设置一个短期内的切换开关。从长远来看,他们设想重新设计食谱页面视图。

Wake Lock 容器

var wakeLockControl = () => {
  return import(/* webpackChunkName: 'wakeLock' */ './wakeLock');
};

export default {
  components: {
    wakeLockControl: wakeLockControl,
  },
  data() {
    return {
      config: {},
      wakeLockComponent: '',
    };
  },
  methods: {
    init: function(config) {
      this.config = config || {};
      if ('wakeLock' in navigator && 'request' in navigator.wakeLock) {
        this.wakeLockComponent = 'wakeLockControl';
      } else {
        console.log('Browser not supported');
      }
    },
  },
};

Wake Lock 组件

<template>
  <div class="wakeLock">
    <div class="textAbove"></div>
    <label class="switch" :aria-label="settingsInternal.textAbove">
      <input type="checkbox" @change="onChange()" v-model="isChecked">
      <span class="slider round"></span>
    </label>
  </div>
</template>

<script type="text/javascript">
  import debounce from 'lodash.debounce';

  const scrollDebounceMs = 1000;

  export default {
    props: {
      settings: { type: Object },
    },
    data() {
      return {
        settingsInternal: this.settings || {},
        isChecked: false,
        wakeLock: null,
        timerId: 0,
      };
    },
    created() {
      this.$_raiseAnalyticsEvent('Wake Lock Toggle Available');
    },
    methods: {
      onChange: function() {
        if (this.isChecked) {
          this.$_requestWakeLock();
        } else {
          this.$_releaseWakeLock();
        }
      },
      $_requestWakeLock: async function() {
        try {
          this.wakeLock = await navigator.wakeLock.request('screen');
          //Start new timer
          this.$_handleAbortTimer();
          //Only add event listeners after wake lock is successfully enabled
          document.addEventListener(
            'visibilitychange',
            this.$_handleVisibilityChange,
          );
          window.addEventListener(
            'scroll',
            debounce(this.$_handleAbortTimer, scrollDebounceMs),
          );
          this.$_raiseAnalyticsEvent('Wake Lock Toggle Enabled');
        } catch (e) {
          this.isChecked = false;
        }
      },
      $_releaseWakeLock: function() {
        try {
          this.wakeLock.release();
          this.wakeLock = null;
          //Clear timer
          this.$_handleAbortTimer();
          //Clean up event listeners
          document.removeEventListener(
            'visibilitychange',
            this.$_handleVisibilityChange,
          );
          window.removeEventListener(
            'scroll',
            debounce(this.$_handleAbortTimer, scrollDebounceMs),
          );
        } catch (e) {
          console.log(`Wake Lock Release Error: ${e.name}, ${e.message}`);
        }
      },
      $_handleAbortTimer: function() {
        //If there is an existing timer then clear it and set to zero
        if (this.timerId !== 0) {
          clearTimeout(this.timerId);
          this.timerId = 0;
        }
        //Start new timer; Will be triggered from toggle enabled or scroll event
        if (this.isChecked) {
          this.timerId = setTimeout(
            this.$_releaseWakeLock,
            this.settingsInternal.timeoutDurationMs,
          );
        }
      },
      $_handleVisibilityChange: function() {
        //Handle navigating away from page/tab
        if (this.isChecked) {
          this.$_releaseWakeLock();
          this.isChecked = false;
        }
      },
      $_raiseAnalyticsEvent: function(eventType) {
        let eventParams = {
          EventType: eventType,
          Position: window.location.pathname || '',
        };
        Analytics.raiseEvent(eventParams);
      },
    },
  };
</script>

结果

Vue.js 组件已部署在所有三个站点上,并取得了出色的效果。在 2019 年 12 月 10 日至 2020 年 1 月 10 日期间,BettyCrocker.com 报告了以下指标

  • 在所有浏览器与 Wake Lock API 兼容的 Betty Crocker 用户中,3.5% 的用户立即启用了该功能,使其成为前 5 大操作之一。
  • 启用 Wake Lock 的用户的会话时长比未启用 Wake Lock 的用户长 3.1 倍。
  • 启用 Wake Lock 的用户的跳出率比未使用 Wake Lock 功能的用户低 50%。
  • 与所有用户相比,Wake Lock 用户的购买意向指标高出约 300%。

3.1×

更长的会话时长

50%

更低的跳出率

300%

更高的购买意向指标

结论

Betty Crocker 在使用 Wake Lock API 后看到了出色的效果。您可以通过在其任何站点(BettyCrockerPillsburyTablespoon)上搜索您最喜欢的食谱并启用烹饪时防止屏幕变暗开关来亲自测试该功能。

Wake Lock 的用例不仅限于食谱站点。其他示例包括需要保持屏幕开启直到条形码被扫描的登机牌或票务应用、持续保持屏幕开启的自助服务亭式应用或防止屏幕在演示期间休眠的基于 Web 的演示应用。

我们已在本网站的一篇综合文章中汇编了关于 Wake Lock API 您需要了解的一切信息。祝您阅读愉快,烹饪愉快!

致谢

揉面团的人照片由 Julian HochgesangUnsplash 上提供。