Cách đồng bộ hoá dữ liệu định kỳ ở chế độ nền

Cecilia Cong
Cecilia Cong

Phong cách hiện đại

Tính năng đồng bộ hoá định kỳ ở chế độ nền cho phép bạn hiển thị nội dung mới khi khởi chạy một trang dựa trên trình chạy dịch vụ hoặc ứng dụng web tiến bộ được khởi chạy. Trình phân tích cú pháp thực hiện việc này bằng cách tải dữ liệu xuống ở chế độ nền khi bạn không dùng ứng dụng hoặc trang.

Sử dụng API đồng bộ hoá định kỳ ở chế độ nền

Sau khi cài đặt trình chạy dịch vụ, hãy sử dụng Permissions API (API Quyền) để truy vấn periodic-background-sync. Bạn có thể thực hiện việc này từ một cửa sổ hoặc ngữ cảnh trình chạy dịch vụ.

const status = await navigator.permissions.query({
  name: 'periodic-background-sync',
});
if (status.state === 'granted') {
  // Periodic background sync can be used.
} else {
  // Periodic background sync cannot be used.
}

Việc đăng ký đồng bộ hoá định kỳ yêu cầu cả thẻ và khoảng thời gian đồng bộ hoá tối thiểu (minInterval). Thẻ xác định lượt đồng bộ hoá đã đăng ký để có thể đăng ký nhiều lượt đồng bộ hoá. Trong ví dụ bên dưới, tên thẻ là 'content-sync'minInterval là một ngày.

navigator.serviceWorker.ready.then(async registration => {
  try {
    await registration.periodicSync.register('get-cats', { minInterval: 24 * 60 * 60 * 1000 });
    console.log(Periodic background sync registered.');
  } catch (err) {
    console.error(err.name, err.message);
  }
});

Gọi periodicSync.getTags() để truy xuất một mảng thẻ đăng ký. Ví dụ bên dưới sử dụng tên thẻ để xác nhận rằng tính năng cập nhật bộ nhớ đệm đang hoạt động nhằm tránh cập nhật lại.

const registration = await navigator.serviceWorker.ready;
if ('periodicSync' in registration) {
  const tags = await registration.periodicSync.getTags();
  // Only update content if sync isn't set up.
  if (!tags.includes('content-sync')) {
    updateContentOnPageLoad();
  }
} else {
  // If periodic background sync isn't supported, always update.
  updateContentOnPageLoad();
}

Để phản hồi một sự kiện đồng bộ hoá định kỳ ở chế độ nền, hãy thêm một trình xử lý sự kiện periodicsync vào trình chạy dịch vụ. Đối tượng sự kiện được chuyển đến sự kiện đó sẽ chứa thông số thẻ khớp với giá trị được dùng trong quá trình đăng ký. Ví dụ: nếu quá trình đồng bộ hoá định kỳ ở chế độ nền được đăng ký với tên 'content-sync', thì event.tag sẽ là 'content-sync'.

self.addEventListener('periodicsync', (event) => {
  if (event.tag === 'content-sync') {
    event.waitUntil(syncContent());
  }
});

Khả năng tương thích với trình duyệt

Hỗ trợ trình duyệt

  • 80
  • 80
  • x
  • x

Nguồn

Cách cổ điển

Thay vì cập nhật dữ liệu trong nền để dữ liệu sẵn sàng khi người dùng tải ứng dụng, cách cũ chỉ đơn giản là cập nhật dữ liệu khi tải.

Tài liệu đọc thêm

Bản minh hoạ

HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link
      rel="icon"
      href=""
    />
    <link rel="manifest" href="./manifest.json" />
    <title>How to periodically synchronize data in the background</title>
    <link rel="stylesheet" href="/style.css" />
    <!-- TODO: Devsite - Removed inline handlers -->
    <!-- <script src="/script.js" defer></script> -->
  </head>
  <body>
    <h1>How to periodically synchronize data in the background</h1>
    <p class="available">Periodic background sync can be used. Install the app first.</p>
    <p class="not-available">Periodic background sync cannot be used.</p>
    <h2>Last updated</h2>
    <p class="last-updated">Never</p>
    <h2>Registered tags</h2>
    <ul>
      <li>None yet.</li>
    </ul>
  </body>
</html>

CSS


        html {
  box-sizing: border-box;
  font-family: system-ui, sans-serif;
  color-scheme: dark light;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

body {
  margin: 1rem;
}
        

JS


        const available = document.querySelector('.available');
const notAvailable = document.querySelector('.not-available');
const ul = document.querySelector('ul');
const lastUpdated = document.querySelector('.last-updated');

const updateContent = async () => {
  const data = await fetch(
    'https://worldtimeapi.org/api/timezone/Europe/London.json'
  ).then((response) => response.json());
  return new Date(data.unixtime * 1000);
};

const registerPeriodicBackgroundSync = async (registration) => {
  const status = await navigator.permissions.query({
    name: 'periodic-background-sync',
  });
  if (status.state === 'granted' && 'periodicSync' in registration) {
    try {
      // Register the periodic background sync.
      await registration.periodicSync.register('content-sync', {
        // An interval of one day.
        minInterval: 24 * 60 * 60 * 1000,
      });
      available.hidden = false;
      notAvailable.hidden = true;

      // List registered periodic background sync tags.
      const tags = await registration.periodicSync.getTags();
      if (tags.length) {
        ul.innerHTML = '';
      }
      tags.forEach((tag) => {
        const li = document.createElement('li');
        li.textContent = tag;
        ul.append(li);
      });

      // Update the user interface with the last periodic background sync data.
      const backgroundSyncCache = await caches.open('periodic-background-sync');
      if (backgroundSyncCache) {
        const backgroundSyncResponse =
          backgroundSyncCache.match('/last-updated');
        if (backgroundSyncResponse) {
          lastUpdated.textContent = `${await fetch('/last-updated').then(
            (response) => response.text()
          )} (periodic background-sync)`;
        }
      }

      // Listen for incoming periodic background sync messages.
      navigator.serviceWorker.addEventListener('message', async (event) => {
        if (event.data.tag === 'content-sync') {
          lastUpdated.textContent = `${await updateContent()} (periodic background sync)`;
        }
      });
    } catch (err) {
      console.error(err.name, err.message);
      available.hidden = true;
      notAvailable.hidden = false;
      lastUpdated.textContent = 'Never';
    }
  } else {
    available.hidden = true;
    notAvailable.hidden = false;
    lastUpdated.textContent = `${await updateContent()} (manual)`;
  }
};

if ('serviceWorker' in navigator) {
  window.addEventListener('load', async () => {
    const registration = await navigator.serviceWorker.register('./sw.js');
    console.log('Service worker registered for scope', registration.scope);

    await registerPeriodicBackgroundSync(registration);
  });
}