从 MDN 的 Web API 列表中,我们将介绍 8 个在日常服务开发中有用但可能用得不多的 API。这些 API 可能鲜为人知,但对于某些情况和要求非常有用。

Beacon API

信标(Beacon)可以异步与非阻塞的数据传输,从而最大限度地减少与其他关键操作的资源争用,同时它可以确保这些请求一定会被处理并将其传递到服务端:

  • 信标请求优先避免与关键操作和更高优先级的网络请求竞争。
  • 信标请求可以有效地合并,以优化移动设备上的能量使用。
  • 保证页面卸载之前启动信标请求,并允许运行完成且不会阻塞请求或阻塞处理用户交互事件的任务。

如果浏览器已成功将传递请求排队,则此方法返回true,否则返回false

<!DOCTYPE html>
<html lang="en-US">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=yes">
  <title>Navigator.sendBeacon Demo</title>
</head>
<body>

  <h1>Navigator.sendBeacon Demo</h1>

  <p>Beacon lets you asynchronously transfer small HTTP data from the User Agent to a web server. It can be used e.g. to send analytics or diagnostics code without delaying the page's unload or affecting the performance of the next navigation.</p>

  <p>The sample below sends a beacon to a sample server at <code>https://putsreq.herokuapp.com/4GE2nVUuDoDGsNyKES2G</code> on unload. Close this page and visit the <strong><a href="https://putsreq.herokuapp.com/4GE2nVUuDoDGsNyKES2G/inspect" target="_blank" rel="noreferrer noopener">PutsReq inspect page</a></strong> to see if the beacon data was received.</p>

  <strong>JavaScript snippet</strong>
  <pre>
  <script src="navigator.sendbeacon.min.js"></script>
  <script>
    window.onbeforeunload = function(e) {
      navigator.sendBeacon(
        'https://putsreq.herokuapp.com/4GE2nVUuDoDGsNyKES2G',
        'Sent by a beacon!'
      )

      // empty return is required to not show popup
      return
    }
  </script>
  </pre>

  <script src="../dist/navigator.sendbeacon.min.js"></script>
  <script>
    window.onbeforeunload = function(e) {
      navigator.sendBeacon(
        'https://putsreq.herokuapp.com/4GE2nVUuDoDGsNyKES2G',
        'Sent by a beacon!'
      )

      // empty return is required to not show popup
      return
    }
  </script>
</body>
</html>

支持的浏览器如下

432685b16f40-20230629.png

页面可见性API

您可以使用页面可见性 API 来确定网页是否对用户可见,并根据该状态采取一些操作,可能使用到的场景包括

  • 包含图像幻灯片的网站在用户不看时不应前进到下一张幻灯片
  • 在页面不可见时不想请求服务器进行更新的应用程序
  • 我想检测页面何时预渲染,以便我可以计算准确的页面浏览量。
  • 想要在设备处于待机模式(用户按下电源按钮关闭屏幕)时静音音频的站点。
  • 如果您的 Web 应用程序正在播放视频,请在用户将选项卡置于后台时暂停视频,并在用户返回此选项卡时恢复播放。

此示例在网页可见时将背景颜色设置为绿色,在网页隐藏时将背景颜色设置为红色。这样可以让您直观地掌握页面的显示状态。

<!DOCTYPE html>
<html>
<head>
    <title>Page Visibility API Demo</title>
</head>
<body>
    <script>
    // Function to handle visibility changes
    function handleVisibilityChange() {
        if (document.hidden) {
            document.body.style.backgroundColor = 'red';
        } else {
            document.body.style.backgroundColor = 'green';
        }
    }

    // Add the event listener
    document.addEventListener('visibilitychange', handleVisibilityChange);
    </script>
</body>
</html>

支持的浏览器如下

724932d02aff-20230629.png

推送API

这是一个用于从服务器向浏览器发送推送消息的API。它被用来向用户提供重要的更新信息。下面给出了向Web应用程序发送推送通知的一个基本实现:

1、注册Service Worker

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js')
  .then(function(registration) {
    console.log('Service Worker registration successful with scope: ', registration.scope);
  })
  .catch(function(err) {
    console.log('Service Worker registration failed: ', err);
  });
}

2、请求用户授权推送通知

function askPermission() {
  return new Promise(function(resolve, reject) {
    const permissionResult = Notification.requestPermission(function(result) {
      resolve(result);
    });

    if (permissionResult) {
      permissionResult.then(resolve, reject);
    }
  })
  .then(function(permissionResult) {
    if (permissionResult !== 'granted') {
      throw new Error('We weren\'t granted permission.');
    }
  });
}

3、获取推送订阅

function subscribeUserToPush() {
  return navigator.serviceWorker.register('service-worker.js')
  .then(function(registration) {
    const subscribeOptions = {
      userVisibleOnly: true,
      applicationServerKey: urlBase64ToUint8Array(
        'BEl62iUYgUivxIkv69yViUuiCOaI9-EbH-Rj_P7fAaY5Zi6k5EN8YkkgDLoU2YYzmp0nU8MJMGUZYkUZbT7dx5A'
      )
    };

    return registration.pushManager.subscribe(subscribeOptions);
  })
  .then(function(pushSubscription) {
    console.log('Received PushSubscription: ', JSON.stringify(pushSubscription));
    return pushSubscription;
  });
}

4、使用 Service Worker 处理推送事件

self.addEventListener('push', function(event) {
  if (event.data) {
    console.log('This push event has data: ', event.data.text());
  } else {
    console.log('This push event has no data.');
  }

  const title = 'Hello';
  const options = {
    body: 'World'
  };

  event.waitUntil(self.registration.showNotification(title, options));
});

每当推送事件发送到服务工作人员时,此代码都会在浏览器中显示通知。

支持的浏览器如下

api.PushEvent

c4e272b691a0-20230705.png

api.PushMessageData

e2dd728484cd-20230705.png

Service Workers API

Service Worker 是在网页后面运行的脚本,可以处理网页和浏览器之间的网络请求,或者提供完全离线工作的能力。下面是注册和安装基本 Service Worker 的示例。

首先,要注册您的 Service Worker,您可以在主 JavaScript 文件中编写如下代码

// Check if service workers are supported
if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('/service-worker.js')
    .then(function(registration) {
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    })
    .catch(function(error) {
      console.log('ServiceWorker registration failed: ', error);
    });
  });
}

如果浏览器支持 Service Worker,上面的代码将在页面完全加载时注册 Service Worker。要注册的 Service Worker 由路径 /service-worker.js 指定,并根据注册是否成功将结果打印到控制台。

接下来,在 service-worker.js 中控制 Service Worker 生命周期事件:

// The install handler takes care of precaching the resources we always need.
self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('my-cache').then(function(cache) {
      return cache.addAll([
        '/',
        '/index.html',
        '/styles/main.css',
        '/script/main.js'
      ]);
    })
  );
});

// The activate handler takes care of cleaning up old caches.
self.addEventListener('activate', function(event) {
  var cacheWhitelist = ['my-cache'];

  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.map(function(cacheName) {
          if (cacheWhitelist.indexOf(cacheName) === -1) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

// The fetch handler serves responses for same-origin resources from a cache.
self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function(response) {
      return response || fetch(event.request);
    })
  );
});

上面的 service-worker.js 定义了一个进程,该进程在安装 Service Worker 时缓存某些文件,在激活时删除旧缓存,并尝试从缓存中响应每个网络请求。我在这里。

通过应用这些基本示例,可以使用 Service Worker 实现各种流程。例如,可以与Push API结合实现推送通知,或者与Background Sync API结合实现后台同步。

IndexedDB

IndexedDB 是一个存储在 Web 浏览器中的键值数据库系统。使用 IndexedDB API 创建数据库并存储、检索和删除对象的基本代码如下所示

// 设置数据库的名称和版本
var dbName = 'myDatabase';
var dbVersion = 1;

// 打开(或创建)数据库
var request = indexedDB.open(dbName, dbVersion);

request.onerror = function(event) {
console.log('数据库错误: ' + event.target.errorCode);
};

request.onupgradeneeded = function(event) {
var db = event.target.result;
// 创建名为"myObjects"的对象存储
var objectStore = db.createObjectStore('myObjects', {keyPath: 'id'});

// 创建索引以启用高效搜索和排序
objectStore.createIndex('name', 'name', {unique: false});
};

request.onsuccess = function(event) {
console.log('数据库初始化成功');

};

接下来是将数据添加到对象存储的代码。

var db;
var request = indexedDB.open('myDatabase');

request.onsuccess = function(event) {
  db = event.target.result;

  var transaction = db.transaction(['myObjects'], 'readwrite');
  var objectStore = transaction.objectStore('myObjects');
  var request = objectStore.add({id: 1, name: 'myObject'});

  request.onsuccess = function(event) {
    console.log('Object added successfully');
  };

  request.onerror = function(event) {
    console.log('Error adding object');
  };
};

最后,这是检索数据的代码。

var db;
var request = indexedDB.open('myDatabase');

request.onsuccess = function(event) {
  db = event.target.result;

  var transaction = db.transaction(['myObjects']);
  var objectStore = transaction.objectStore('myObjects');
  var request = objectStore.get(1);

  request.onsuccess = function(event) {
    var result = event.target.result;
    console.log(result); // {id: 1, name: 'myObject'}
  };

  request.onerror = function(event) {
    console.log('Error getting object');
  };
};

通过应用这些基本代码,可以使用 IndexedDB 存储、检索、删除、更新等数据。