Tư duy của nhân viên dịch vụ

Tư duy như thế nào khi nghĩ về trình chạy dịch vụ.

Service worker rất mạnh mẽ và hoàn toàn đáng để học. Chúng giúp bạn mang đến trải nghiệm hoàn toàn mới cho người dùng. Trang web của bạn có thể tải ngay lập tức. Tính năng này có thể hoạt động ngoại tuyến. Người dùng có thể cài đặt phiên bản này dưới dạng một ứng dụng dành riêng cho nền tảng và mọi thứ đều hoàn thiện — nhưng với phạm vi tiếp cận và sự tự do của web.

Nhưng Service worker không giống bất cứ thứ gì mà hầu hết các nhà phát triển web của chúng ta đã quen thuộc. Các trò chơi này đi kèm với quá trình tìm hiểu phức tạp và một vài trở ngại mà bạn phải cẩn trọng.

Gần đây, Google Developers và tôi đã cộng tác trong dự án Service Workies — một trò chơi miễn phí giúp tìm hiểu về trình chạy dịch vụ. Trong khi xây dựng công trình và xử lý mọi chi tiết phức tạp của nhân viên dịch vụ, tôi đã gặp phải một vài vấn đề. Điều giúp tôi nhiều nhất là nghĩ ra một số phép ẩn dụ khắc hoạ. Trong bài đăng này, chúng ta sẽ khám phá những mô hình tư duy này và khám phá những đặc điểm nghịch lý khiến nhân viên dịch vụ vừa khéo léo vừa tuyệt vời.

Giống nhau, nhưng khác

Trong khi lập trình trình chạy dịch vụ, bạn sẽ cảm thấy quen thuộc với nhiều thứ. Bạn có thể sử dụng các tính năng ngôn ngữ JavaScript mới mà mình yêu thích. Bạn theo dõi các sự kiện trong vòng đời giống như các sự kiện giao diện người dùng. Bạn quản lý quy trình kiểm soát với những lời hứa như bạn đã quen thuộc.

Tuy nhiên, hành vi khác của trình chạy dịch vụ khiến bạn bối rối rối loạn. Đặc biệt là khi bạn làm mới trang và không thấy các thay đổi mã của mình được áp dụng.

Lớp mới

Thông thường, khi xây dựng một trang web bạn chỉ cần nghĩ đến hai lớp: ứng dụng khách và máy chủ. Nhân viên dịch vụ là một lớp hoàn toàn mới nằm ở giữa.

Service worker đóng vai trò là lớp trung gian giữa ứng dụng và máy chủ

Hãy xem trình chạy dịch vụ như một loại tiện ích trình duyệt—tiện ích mà trang web của bạn có thể cài đặt trong trình duyệt của người dùng. Sau khi cài đặt, service worker extends trình duyệt cho trang web của bạn bằng một lớp giữa mạnh mẽ. Lớp trình chạy dịch vụ này có thể chặn và xử lý tất cả các yêu cầu mà trang web của bạn đưa ra.

Lớp trình chạy dịch vụ có vòng đời riêng độc lập với thẻ trình duyệt. Một thao tác làm mới trang đơn giản là chưa đủ để cập nhật một trình chạy dịch vụ – giống như việc bạn không mong đợi một thao tác làm mới trang để cập nhật mã được triển khai trên máy chủ. Mỗi lớp có các quy tắc cập nhật riêng biệt.

Trong trò chơi Service Workies, chúng ta sẽ đề cập đến nhiều thông tin chi tiết về vòng đời của trình chạy dịch vụ và cho bạn rất nhiều phương pháp thực hành để xử lý vòng đời này.

Mạnh mẽ nhưng bị hạn chế

Việc có một nhân viên dịch vụ trên trang web của bạn sẽ mang lại cho bạn những lợi ích đáng kinh ngạc. Trang web của bạn có thể:

  • hoạt động hoàn hảo ngay cả khi người dùng không kết nối mạng
  • nhận được những điểm cải tiến về hiệu suất mạnh mẽ thông qua việc lưu vào bộ nhớ đệm
  • sử dụng thông báo đẩy
  • được cài đặt dưới dạng PWA

Với khả năng của trình chạy dịch vụ, chúng bị hạn chế về mặt thiết kế. Các công cụ này không thể thực hiện thao tác đồng bộ hoặc trong cùng một chuỗi với trang web. Tức là bạn không có quyền truy cập vào:

  • localStorage
  • DOM
  • cửa sổ

Tin vui là trang của bạn có thể giao tiếp với nhân viên dịch vụ bằng nhiều cách, trong đó có postMessage trực tiếp, Kênh tin nhắn giữa hai người và Kênh truyền phát một với nhiều.

Tồn tại lâu dài nhưng ngắn hạn

Một trình chạy dịch vụ đang hoạt động vẫn tiếp tục ngay cả sau khi người dùng rời khỏi trang web của bạn hoặc đóng thẻ. Trình duyệt lưu giữ trình chạy dịch vụ này để trình chạy này sẵn sàng vào lần tiếp theo người dùng quay lại trang web của bạn. Trước khi đưa ra yêu cầu đầu tiên, service worker sẽ có cơ hội chặn yêu cầu và kiểm soát trang. Đây là cách cho phép một trang web hoạt động khi không có mạng – dịch vụ có thể phân phát phiên bản được lưu vào bộ nhớ đệm của trang, ngay cả khi người dùng không có kết nối Internet.

Trong phần Service Workies, chúng ta trực quan hoá khái niệm này bằng Kolohe (một trình chạy dịch vụ thân thiện) có khả năng chặn và xử lý các yêu cầu.

Đã dừng

Mặc dù worker có vẻ là bất tử, nhưng họ có thể bị dừng bất cứ lúc nào. Trình duyệt không muốn lãng phí tài nguyên vào một trình chạy dịch vụ hiện không làm gì cả. Việc bị dừng không giống như bị chấm dứt – trình chạy dịch vụ vẫn được cài đặt và kích hoạt. thế là bạn đã ngủ thiếp đi rồi. Vào lần tiếp theo cần thiết (ví dụ: để xử lý yêu cầu), trình duyệt sẽ đánh thức nó khởi động lại.

waitUntil

Do liên tục phải ngủ, nhân viên dịch vụ của bạn cần có một cách để cho trình duyệt biết khi nào trình duyệt đang làm việc gì đó quan trọng và không cảm thấy muốn chợp mắt. Đây là lúc event.waitUntil() phát huy tác dụng. Phương thức này kéo dài vòng đời sử dụng, giúp cho phương thức không bị dừng và chuyển sang giai đoạn tiếp theo trong vòng đời cho đến khi chúng ta sẵn sàng. Việc này giúp chúng tôi có thời gian để thiết lập bộ nhớ đệm, tìm nạp tài nguyên từ mạng, v.v.

Ví dụ này cho trình duyệt biết rằng trình chạy dịch vụ vẫn chưa cài đặt xong cho đến khi bộ nhớ đệm assets được tạo và điền sẵn hình ảnh một thanh kiếm:

self.addEventListener("install", event => {
  event.waitUntil(
    caches.open("assets").then(cache => {
      return cache.addAll(["/weapons/sword/blade.png"]);
    })
  );
});

Cẩn thận với trạng thái toàn cầu

Khi quá trình bắt đầu/dừng này diễn ra, phạm vi toàn cục của trình chạy dịch vụ sẽ được đặt lại. Vì vậy, hãy cẩn thận để không sử dụng bất kỳ trạng thái toàn cục nào trong trình chạy dịch vụ, nếu không bạn sẽ thấy buồn vào lần tiếp theo nó bật lại và có trạng thái khác với kỳ vọng.

Hãy xem xét ví dụ sử dụng trạng thái toàn cục sau:

const favoriteNumber = Math.random();
let hasHandledARequest = false;

self.addEventListener("fetch", event => {
  console.log(favoriteNumber);
  console.log(hasHandledARequest);
  hasHandledARequest = true;
});

Trên mỗi yêu cầu, trình chạy dịch vụ này sẽ ghi lại một số – giả sử 0.13981866382421893. Biến hasHandledARequest cũng thay đổi thành true. Bây giờ, trình chạy dịch vụ sẽ ở trạng thái rảnh trong một chút, vì vậy, trình duyệt sẽ dừng nó. Lần tiếp theo có yêu cầu, Service worker sẽ được sử dụng lại, vì vậy trình duyệt sẽ đánh thức nó. Tập lệnh của tập lệnh đó sẽ được đánh giá lại. Giờ đây, hasHandledARequest được đặt lại thành falsefavoriteNumber là một giao diện hoàn toàn khác – 0.5907281835659033.

Bạn không thể dựa vào trạng thái đã lưu trữ trong trình chạy dịch vụ. Ngoài ra, việc tạo các phiên bản như Kênh tin nhắn cũng có thể gây ra lỗi: bạn sẽ nhận được một phiên bản hoàn toàn mới mỗi khi trình chạy dịch vụ dừng/bắt đầu.

Trong Service Workies chương 3, chúng ta hình dung trình chạy dịch vụ bị dừng bị mất tất cả màu sắc trong khi chờ được đánh thức.

hình ảnh một trình chạy dịch vụ đã dừng

Cùng nhau, nhưng tách biệt

Trang của bạn chỉ có thể được kiểm soát bởi một nhân viên dịch vụ tại một thời điểm. Tuy nhiên, có thể cài đặt hai trình chạy dịch vụ cùng lúc. Khi thay đổi mã trình chạy dịch vụ và làm mới trang, bạn sẽ không thực sự chỉnh sửa trình chạy dịch vụ của mình. Trình chạy dịch vụ không thể thay đổi. Thay vào đó, bạn đang tạo một giao diện hoàn toàn mới. Trình chạy dịch vụ mới này (hãy gọi SW2) sẽ cài đặt nhưng chưa kích hoạt. Lớp này phải chờ để trình chạy dịch vụ hiện tại (SW1) chấm dứt (khi người dùng rời khỏi trang web của bạn).

Kết hợp với bộ nhớ đệm của một trình chạy dịch vụ khác

Trong khi cài đặt, SW2 có thể thiết lập mọi thứ, thường là tạo và điền sẵn bộ nhớ đệm. Tuy nhiên, lưu ý: trình chạy dịch vụ mới này có quyền truy cập vào mọi dữ liệu mà trình chạy dịch vụ hiện tại có quyền truy cập. Nếu bạn không cẩn thận, nhân viên dịch vụ đang chờ mới có thể thực sự gây xáo trộn mọi thứ của nhân viên dịch vụ hiện tại. Một số ví dụ có thể gây rắc rối cho bạn:

  • SW2 có thể xoá bộ nhớ đệm mà SW1 đang sử dụng.
  • SW2 có thể chỉnh sửa nội dung của bộ nhớ đệm mà SW1 đang sử dụng, khiến SW1 phản hồi với các thành phần mà trang không mong muốn.

Bỏ qua, đang chờ

Trình chạy dịch vụ cũng có thể sử dụng phương thức skipWaiting() gây rủi ro để kiểm soát trang ngay sau khi cài đặt xong. Nhìn chung, đây là một ý tưởng không hay trừ phi bạn cố ý cố gắng thay thế một trình chạy dịch vụ lỗi. Trình chạy dịch vụ mới có thể đang sử dụng tài nguyên được cập nhật mà trang hiện tại không mong muốn, dẫn đến lỗi và lỗi.

Bắt đầu dọn dẹp

Cách để ngăn Service worker của bạn không can thiệp vào nhau là đảm bảo chúng sử dụng các bộ nhớ đệm khác nhau. Cách dễ nhất để thực hiện việc đó là tạo phiên bản cho tên bộ nhớ đệm mà chúng sử dụng.

const version = 1;
const assetCacheName = `assets-${version}`;

self.addEventListener("install", event => {
  caches.open(assetCacheName).then(cache => {
    // confidently do stuff with your very own cache
  });
});

Khi triển khai một trình chạy dịch vụ mới, bạn sẽ tăng version để nó thực hiện những việc cần thiết với một bộ nhớ đệm hoàn toàn tách biệt với trình chạy dịch vụ trước đó.

trực quan hoá bộ nhớ đệm

Kết thúc dọn dẹp

Sau khi trình chạy dịch vụ đạt đến trạng thái activated, bạn sẽ biết rằng trình chạy này đã tiếp quản và trình chạy dịch vụ trước đó là thừa. Tại thời điểm này, bạn cần dọn dẹp sau worker cũ. Việc này không chỉ giúp tuân thủ giới hạn bộ nhớ bộ nhớ đệm của người dùng mà còn có thể ngăn chặn các lỗi ngoài ý muốn.

Phương thức caches.match() là lối tắt thường dùng để truy xuất một mục từ bộ nhớ đệm bất kỳ có kết quả trùng khớp. Tuy nhiên, quá trình này sẽ lặp lại qua các bộ nhớ đệm theo thứ tự tạo. Giả sử bạn có hai phiên bản của tệp tập lệnh app.js trong hai bộ nhớ đệm khác nhau là assets-1assets-2. Trang của bạn đang đợi tập lệnh mới hơn được lưu trữ trong assets-2. Tuy nhiên, nếu bạn chưa xoá bộ nhớ đệm cũ thì caches.match('app.js') sẽ trả về bộ nhớ đệm cũ từ assets-1 và rất có thể sẽ làm hỏng trang web của bạn.

Tất cả những gì cần làm để dọn dẹp sau khi chạy trình chạy dịch vụ trước đó là xoá mọi bộ nhớ đệm mà trình chạy dịch vụ mới không cần đến:

const version = 2;
const assetCacheName = `assets-${version}`;

self.addEventListener("activate", event => {
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.map(cacheName => {
          if (cacheName !== assetCacheName){
            return caches.delete(cacheName);
          }
        });
      );
    });
  );
});

Ngăn chặn nhân viên dịch vụ của bạn theo dõi lẫn nhau cần một chút công sức và kỷ luật nhưng rất đáng để thử thách.

Tư duy của trình chạy dịch vụ

Việc duy trì tư duy đúng đắn khi suy nghĩ về nhân viên dịch vụ sẽ giúp bạn tự tin xây dựng cho mình. Sau khi thông thạo các tính năng này, bạn sẽ có thể tạo ra những trải nghiệm ấn tượng cho người dùng của mình.

Nếu bạn muốn hiểu tất cả những điều này bằng cách chơi trò chơi, thì bạn thật may mắn! Hãy chơi Service Workies, nơi bạn sẽ tìm hiểu cách của nhân viên dịch vụ để tiêu diệt quái vật ngoại tuyến.