Xử lý sự kiện bằng trình chạy dịch vụ

Hướng dẫn đề cập đến các khái niệm về trình chạy dịch vụ tiện ích

Tổng quan

Hướng dẫn này giới thiệu về trình chạy dịch vụ Tiện ích của Chrome. Trong hướng dẫn này, bạn sẽ xây dựng một tiện ích cho phép người dùng nhanh chóng điều hướng đến các trang tài liệu tham khảo API của Chrome bằng thanh địa chỉ. Bạn sẽ tìm hiểu cách:

  • Đăng ký trình chạy dịch vụ và nhập các mô-đun.
  • Gỡ lỗi trình chạy dịch vụ tiện ích.
  • Quản lý trạng thái và xử lý sự kiện.
  • Kích hoạt các sự kiện định kỳ.
  • Giao tiếp với kịch bản nội dung.

Trước khi bắt đầu

Hướng dẫn này giả định rằng bạn có kinh nghiệm phát triển web cơ bản. Bạn nên xem lại Tiện ích 101Hello World để biết giới thiệu về việc phát triển tiện ích.

Tạo tiện ích

Bắt đầu bằng cách tạo một thư mục mới có tên là quick-api-reference để lưu giữ các tệp tiện ích hoặc tải mã nguồn xuống từ kho lưu trữ mẫu GitHub của chúng tôi.

Bước 1: Đăng ký trình chạy dịch vụ

Tạo tệp manifest (tệp kê khai) trong thư mục gốc của dự án và thêm mã sau:

manifest.json:

{
  "manifest_version": 3,
  "name": "Open extension API reference",
  "version": "1.0.0",
  "icons": {
    "16": "images/icon-16.png",
    "128": "images/icon-128.png"
  },
  "background": {
    "service_worker": "service-worker.js",
  },
}

Các tiện ích sẽ đăng ký trình chạy dịch vụ trong tệp kê khai và chỉ lấy một tệp JavaScript duy nhất. Không cần gọi navigator.serviceWorker.register(), như bạn thực hiện trong một trang web.

Tạo thư mục images rồi tải các biểu tượng xuống thư mục đó.

Hãy xem các bước đầu tiên trong hướng dẫn về Thời gian đọc để tìm hiểu thêm về siêu dữ liệucác biểu tượng của tiện ích trong tệp kê khai.

Bước 2: Nhập nhiều mô-đun trình chạy dịch vụ

Nhân viên dịch vụ của chúng tôi triển khai hai tính năng. Để đảm bảo khả năng bảo trì tốt hơn, chúng tôi sẽ triển khai từng tính năng trong một mô-đun riêng. Trước tiên, chúng ta cần khai báo trình chạy dịch vụ là một Mô-đun ES trong tệp kê khai. Việc này cho phép chúng ta nhập các mô-đun vào trình chạy dịch vụ của mình:

manifest.json:

{
 "background": {
    "service_worker": "service-worker.js",
    "type": "module"
  },
}

Tạo tệp service-worker.js và nhập 2 mô-đun:

import './sw-omnibox.js';
import './sw-tips.js';

Hãy tạo những tệp này và thêm nhật ký bảng điều khiển vào mỗi tệp.

sw-omnibox.js:

console.log("sw-omnibox.js")

sw-tips.js:

console.log("sw-tips.js")

Xem phần Nhập tập lệnh để tìm hiểu về các cách khác để nhập nhiều tệp trong một trình chạy dịch vụ.

Không bắt buộc: Gỡ lỗi trình chạy dịch vụ

Tôi sẽ giải thích cách tìm nhật ký trình chạy dịch vụ và biết khi nào nhật ký của trình chạy dịch vụ đó chấm dứt. Trước tiên, hãy làm theo hướng dẫn để Tải tiện ích đã giải nén.

Sau 30 giây, bạn sẽ thấy "service worker (không hoạt động)", nghĩa là dịch vụ này đã chấm dứt. Nhấp vào đường liên kết "service worker (không hoạt động)" để kiểm tra. Ảnh động sau đây cho thấy điều này.

Bạn có thấy rằng việc kiểm tra nhân viên dịch vụ đã đánh thức chúng không? Việc mở trình chạy dịch vụ trong công cụ cho nhà phát triển sẽ giúp trình chạy này luôn hoạt động. Để đảm bảo tiện ích hoạt động chính xác khi trình chạy dịch vụ bị chấm dứt, hãy nhớ đóng Công cụ cho nhà phát triển.

Bây giờ, hãy phân tích tiện ích để tìm hiểu vị trí xác định lỗi. Một cách để thực hiện việc này là xoá ".js" khỏi mục nhập './sw-omnibox.js' trong tệp service-worker.js. Chrome sẽ không thể đăng ký trình chạy dịch vụ.

Quay lại chrome://extensions rồi làm mới tiện ích. Bạn sẽ thấy 2 lỗi:

Service worker registration failed. Status code: 3.

An unknown error occurred when fetching the script.

Xem phần Gỡ lỗi tiện ích để biết thêm các cách gỡ lỗi trình chạy dịch vụ tiện ích.

Bước 4: Khởi tạo trạng thái

Chrome sẽ tắt trình chạy dịch vụ nếu không cần thiết. Chúng tôi sử dụng API chrome.storage để duy trì trạng thái qua các phiên của trình chạy dịch vụ. Để có quyền truy cập vào bộ nhớ, chúng ta cần yêu cầu quyền trong tệp kê khai:

manifest.json:

{
  ...
  "permissions": ["storage"],
}

Trước tiên, hãy lưu các đề xuất mặc định vào bộ nhớ. Chúng ta có thể khởi tạo trạng thái khi tiện ích được cài đặt lần đầu bằng cách theo dõi sự kiện runtime.onInstalled():

sw-omnibox.js:

...
// Save default API suggestions
chrome.runtime.onInstalled.addListener(({ reason }) => {
  if (reason === 'install') {
    chrome.storage.local.set({
      apiSuggestions: ['tabs', 'storage', 'scripting']
    });
  }
});

Trình chạy dịch vụ không có quyền truy cập trực tiếp vào đối tượng cửa sổ nên không thể sử dụng window.localStorage để lưu trữ các giá trị. Ngoài ra, trình chạy dịch vụ là các môi trường thực thi ngắn hạn; chúng bị chấm dứt nhiều lần trong suốt phiên trình duyệt của người dùng, điều này khiến chúng không tương thích với các biến toàn cục. Thay vào đó, hãy dùng chrome.storage.local để lưu trữ dữ liệu trên máy cục bộ.

Xem bài viết Duy trì dữ liệu thay vì sử dụng biến chung để tìm hiểu về các lựa chọn lưu trữ khác dành cho trình chạy dịch vụ tiện ích.

Bước 5: Đăng ký sự kiện

Tất cả trình nghe sự kiện cần được đăng ký tĩnh trong phạm vi toàn cầu của trình chạy dịch vụ. Nói cách khác, trình nghe sự kiện không nên được lồng vào các hàm không đồng bộ. Bằng cách này, Chrome có thể đảm bảo rằng tất cả trình xử lý sự kiện được khôi phục trong trường hợp trình chạy dịch vụ khởi động lại.

Trong ví dụ này, chúng ta sẽ sử dụng API chrome.omnibox, nhưng trước tiên, chúng ta phải khai báo điều kiện kích hoạt từ khoá thanh địa chỉ trong tệp kê khai:

manifest.json:

{
  ...
  "minimum_chrome_version": "102",
  "omnibox": {
    "keyword": "api"
  },
}

Bây giờ, hãy đăng ký trình nghe sự kiện trên thanh địa chỉ ở cấp cao nhất của tập lệnh. Khi người dùng nhập từ khoá trên thanh địa chỉ (api) vào thanh địa chỉ theo sau là thẻ hoặc dấu cách, Chrome sẽ hiển thị danh sách đề xuất dựa trên từ khoá trong bộ nhớ. Sự kiện onInputChanged() (lấy hoạt động đầu vào hiện tại của người dùng và đối tượng suggestResult) chịu trách nhiệm điền các đề xuất này.

sw-omnibox.js:

...
const URL_CHROME_EXTENSIONS_DOC =
  'https://developer.chrome.com/docs/extensions/reference/';
const NUMBER_OF_PREVIOUS_SEARCHES = 4;

// Display the suggestions after user starts typing
chrome.omnibox.onInputChanged.addListener(async (input, suggest) => {
  await chrome.omnibox.setDefaultSuggestion({
    description: 'Enter a Chrome API or choose from past searches'
  });
  const { apiSuggestions } = await chrome.storage.local.get('apiSuggestions');
  const suggestions = apiSuggestions.map((api) => {
    return { content: api, description: `Open chrome.${api} API` };
  });
  suggest(suggestions);
});

Sau khi người dùng chọn một đề xuất, onInputEntered() sẽ mở trang tài liệu tham khảo API Chrome tương ứng.

sw-omnibox.js:

...
// Open the reference page of the chosen API
chrome.omnibox.onInputEntered.addListener((input) => {
  chrome.tabs.create({ url: URL_CHROME_EXTENSIONS_DOC + input });
  // Save the latest keyword
  updateHistory(input);
});

Hàm updateHistory() nhận thông tin nhập vào thanh địa chỉ và lưu vào storage.local. Bằng cách này, bạn có thể sử dụng cụm từ tìm kiếm gần đây nhất làm đề xuất trên thanh địa chỉ sau.

sw-omnibox.js:

...
async function updateHistory(input) {
  const { apiSuggestions } = await chrome.storage.local.get('apiSuggestions');
  apiSuggestions.unshift(input);
  apiSuggestions.splice(NUMBER_OF_PREVIOUS_SEARCHES);
  return chrome.storage.local.set({ apiSuggestions });
}

Bước 6: Thiết lập sự kiện định kỳ

Các phương thức setTimeout() hoặc setInterval() thường được dùng để thực hiện các tác vụ bị trì hoãn hoặc định kỳ. Tuy nhiên, các API này có thể không thành công vì trình lập lịch biểu sẽ huỷ các đồng hồ hẹn giờ khi trình chạy dịch vụ chấm dứt. Thay vào đó, các tiện ích có thể sử dụng API chrome.alarms.

Bắt đầu bằng cách yêu cầu quyền "alarms" trong tệp kê khai. Ngoài ra, để tìm nạp mẹo về tiện ích tại một vị trí được lưu trữ từ xa, bạn cần yêu cầu quyền của máy chủ:

manifest.json:

{
  ...
  "permissions": ["storage", "alarms"],
  "permissions": ["storage"],
  "host_permissions": ["https://extension-tips.glitch.me/*"],
}

Tiện ích này sẽ tìm nạp tất cả các mẹo, chọn ngẫu nhiên một mẹo rồi lưu vào bộ nhớ. Chúng tôi sẽ tạo một chuông báo. Chuông báo này sẽ được kích hoạt một lần mỗi ngày để cập nhật số tiền boa. Chuông báo sẽ không được lưu khi bạn đóng Chrome. Vì vậy, chúng ta cần kiểm tra xem thông báo có tồn tại hay không và tạo thông báo nếu chưa có.

sw-tips.js:

// Fetch tip & save in storage
const updateTip = async () => {
  const response = await fetch('https://extension-tips.glitch.me/tips.json');
  const tips = await response.json();
  const randomIndex = Math.floor(Math.random() * tips.length);
  return chrome.storage.local.set({ tip: tips[randomIndex] });
};

const ALARM_NAME = 'tip';

// Check if alarm exists to avoid resetting the timer.
// The alarm might be removed when the browser session restarts.
async function createAlarm() {
  const alarm = await chrome.alarms.get(ALARM_NAME);
  if (typeof alarm === 'undefined') {
    chrome.alarms.create(ALARM_NAME, {
      delayInMinutes: 1,
      periodInMinutes: 1440
    });
    updateTip();
  }
}

createAlarm();

// Update tip once a day
chrome.alarms.onAlarm.addListener(updateTip);

Bước 7: Giao tiếp với các ngữ cảnh khác

Tiện ích sử dụng tập lệnh nội dung để đọc và sửa đổi nội dung của trang. Khi người dùng truy cập vào một trang tài liệu tham khảo API của Chrome, tập lệnh nội dung của tiện ích sẽ cập nhật trang đó với mẹo trong ngày. Dịch vụ này gửi thông báo để yêu cầu worker trong ngày từ worker dịch vụ.

Hãy bắt đầu bằng cách khai báo tập lệnh nội dung trong tệp kê khai và thêm mẫu so khớp tương ứng với tài liệu tham khảo về API Chrome.

manifest.json:

{
  ...
  "content_scripts": [
    {
      "matches": ["https://developer.chrome.com/docs/extensions/reference/*"],
      "js": ["content.js"]
    }
  ]
}

Tạo tệp nội dung mới. Mã sau đây sẽ gửi một thông báo đến trình chạy dịch vụ để yêu cầu tính tiền boa. Sau đó, thêm một nút sẽ mở cửa sổ bật lên chứa mẹo về tiện ích. Mã này sử dụng API Popover của nền tảng web mới.

content.js:

(async () => {
  // Sends a message to the service worker and receives a tip in response
  const { tip } = await chrome.runtime.sendMessage({ greeting: 'tip' });

  const nav = document.querySelector('.upper-tabs > nav');
  
  const tipWidget = createDomElement(`
    <button type="button" popovertarget="tip-popover" popovertargetaction="show" style="padding: 0 12px; height: 36px;">
      <span style="display: block; font: var(--devsite-link-font,500 14px/20px var(--devsite-primary-font-family));">Tip</span>
    </button>
  `);

  const popover = createDomElement(
    `<div id='tip-popover' popover style="margin: auto;">${tip}</div>`
  );

  document.body.append(popover);
  nav.append(tipWidget);
})();

function createDomElement(html) {
  const dom = new DOMParser().parseFromString(html, 'text/html');
  return dom.body.firstElementChild;
}

Bước cuối cùng là thêm một trình xử lý tin nhắn vào dịch vụ của chúng ta. Trình xử lý này sẽ gửi câu trả lời đến tập lệnh nội dung kèm theo tiền boa hằng ngày.

sw-tips.js:

...
// Send tip to content script via messaging
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.greeting === 'tip') {
    chrome.storage.local.get('tip').then(sendResponse);
    return true;
  }
});

Kiểm tra xem có hoạt động không

Xác minh rằng cấu trúc tệp của dự án có dạng như sau:

Nội dung của thư mục tiện ích: thư mục images, manifest.json, service-worker.js, sw-omnibox.js, sw-tips.js và content.js

Tải tiện ích của bạn trên thiết bị

Để tải một tiện ích đã giải nén ở chế độ nhà phát triển, hãy làm theo các bước trong Hello world.

Mở trang tham khảo

  1. Nhập từ khoá "api" vào thanh địa chỉ của trình duyệt.
  2. Nhấn "phím tab" hoặc "phím cách".
  3. Nhập tên đầy đủ của API đó.
    • HOẶC chọn từ danh sách tìm kiếm trước đây
  4. Một trang mới sẽ mở ra trang tài liệu tham khảo API của Chrome.

Ứng dụng sẽ hiển thị như sau:

Tài liệu tham khảo API nhanh mở tài liệu tham khảo API thời gian chạy
Tiện ích API nhanh mở API Thời gian chạy.

Mở phần mẹo trong ngày

Nhấp vào nút Mẹo trên thanh điều hướng để mở mẹo tiện ích.

Mở mẹo hằng ngày trong
Tiện ích API nhanh mở thông tin hữu ích trong ngày.

🎯 Có thể cải thiện

Dựa trên những điều bạn học được hôm nay, hãy thử thực hiện một trong những việc sau đây:

  • Khám phá cách khác để triển khai các đề xuất trên thanh địa chỉ.
  • Tạo phương thức tuỳ chỉnh của riêng bạn để hiển thị mẹo của phần mở rộng.
  • Mở một trang bổ sung cho các trang API tham chiếu Tiện ích web của MDN.

Tiếp tục phát triển ứng dụng!

Chúc mừng bạn đã hoàn thành hướng dẫn này 🎉. Hãy tiếp tục nâng cấp kỹ năng của bạn bằng cách hoàn thành các hướng dẫn khác dành cho người mới:

Phần mở rộng Kiến thức bạn sẽ học được
Thời gian đọc Để tự động chèn một phần tử vào một nhóm trang cụ thể.
Trình quản lý thẻ Tạo một cửa sổ bật lên quản lý các thẻ trình duyệt.
Chế độ lấy nét Chạy mã trên trang hiện tại sau khi nhấp vào thao tác với tiện ích.

Tiếp tục khám phá

Để tiếp tục lộ trình học tập dành cho trình chạy dịch vụ tiện ích, bạn nên khám phá các bài viết sau: