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

Cách suy nghĩ về nhân viên dịch vụ.

Trình chạy dịch vụ rất hữu ích và hoàn toàn đáng để học hỏi. Chúng giúp bạn mang đến cho người dùng trải nghiệm hoàn toàn mới. 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 khi không có mạng. Nó có thể được cài đặt như một ứng dụng dành riêng cho nền tảng và mang lại cảm giác chỉn chu – nhưng với phạm vi tiếp cận và sự tự do của web.

Tuy nhiên, Service worker không giống bất cứ điều gì mà hầu hết các nhà phát triển web trong chúng ta đều quen thuộc. Những trò chơi này bạn phải trải qua nhiều giai đoạn học tập và phải tránh một số trở ngại.

Google Developers và tôi gần đây đã cộng tác về một dự án Service Workies, một trò chơi miễn phí giúp tìm hiểu trình chạy dịch vụ. Trong quá trình xây dựng và làm việc với các nhân viên dịch vụ phức tạp, tôi đã gặp phải một số khó khăn. Điều giúp tôi nhiều nhất là nghĩ ra một số phép ẩn dụ giàu tính 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à cùng suy nghĩ về những đặc điểm nghịch lý khiến nhân viên dịch vụ vừa khó khăn vừa tuyệt vời.

Giống nhau, nhưng khác

Trong khi viết mã cho worker, bạn sẽ thấy có nhiều thứ quen thuộc. Bạn sẽ 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 tương tự 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 bằng cách hứa hẹn như bạn đã quen thuộc.

Nhưng hành vi khác của trình chạy dịch vụ khiến bạn phải gật đầu bối rối. Đặc biệt là khi bạn làm mới trang và không thấy các thay đổi đối với mã được áp dụng.

Một 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ủ. Service worker là một lớp hoàn toàn mới nằm ở giữa.

Trình chạy dịch vụ đóng vai trò là lớp giữa giữa ứng dụng và máy chủ

Hãy xem trình chạy dịch vụ là một loại tiện ích của trình duyệt—mộ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, trình chạy dịch vụ sẽ mở rộng trình duyệt cho trang web của bạn với 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. Việc làm mới trang chỉ đơn giản là chưa đủ để cập nhật trình chạy dịch vụ – cũng giống như việc bạn không mong đợi việ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.

Trong trò chơi Service Workies, chúng ta sẽ đề cập đến nhiều 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 với nó.

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 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 có kết nối mạng
  • có được những cải tiến lớn về hiệu suất thông qua lưu vào bộ nhớ đệm
  • sử dụng thông báo đẩy
  • được cài đặt dưới dạng PWA

Dịch vụ có thể làm được nhiều nhất, nhưng bị hạn chế bởi thiết kế. Chúng 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 của bạn. Điều đó có nghĩa 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ó một số cách để giao tiếp với trình chạy dịch vụ, trong đó có postMessage trực tiếp, Kênh thông báo trực tiếp và nhiều Kênh truyền phát.

Dài hạn nhưng ngắn hạn

Trình chạy dịch vụ đang hoạt động sẽ tiếp tục hoạt động 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 giữ trình chạy dịch vụ này để trình duyệt 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 yêu cầu đầu tiên được thực hiện, service worker có cơ hội để chặn nó và kiểm soát trang. Đây là điều cho phép một trang web hoạt động khi không có mạng—trình chạy dịch vụ có thể tự 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 Service Workies, chúng tôi trực quan hoá khái niệm này khi Kolohe (một nhân viên dịch vụ thân thiện) chặn và xử lý các yêu cầu.

Đã dừng

Mặc dù các nhân viên dịch vụ có vẻ như bất tử, nhưng họ có thể ngừng việc 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 thực hiện bất kỳ chức năng nào. Việc bị dừng không giống như việc bị chấm dứt – trình chạy dịch vụ vẫn được cài đặt và kích hoạt. Tôi đã ngủ 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ó sao lưu.

waitUntil

Do khả năng liên tục phải chìm vào giấc ngủ, nên nhân viên dịch vụ của bạn cần một cách nào đó để cho trình duyệt biết khi nào trình duyệt đang thực hiện tác vụ quan trọng nào đó và không cảm thấy như đang chợp mắt. Đây là lúc event.waitUntil() phát huy tác dụng. Phương thức này mở rộng vòng đời được sử dụng, giúp phương thức này 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 sẽ giúp chúng ta 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ụ của chúng ta 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"]);
    })
  );
});

Chú ý đến tình trạng toàn cầu

Khi việc bắt đầu/dừng này xảy ra, phạm vi chung 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ụ của bạn, nếu không, bạn sẽ thấy buồn vào lần tiếp theo nó được đánh thức trở lại và có trạng thái khác với trạng thái mong đợi.

Hãy xem xét ví dụ sau đây về việc sử dụng trạng thái chung:

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

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

Trong 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ẽ không hoạt động trong một chút, vì vậy trình duyệt sẽ dừng nó. Vào lần tiếp theo có yêu cầu, trình chạy dịch vụ sẽ cần thiết một lần nữa, vì vậy trình duyệt sẽ đánh thức trình chạy dịch vụ đó. Tập lệnh của tệp đã được đánh giá lại. Hiện tại, hasHandledARequest được đặt lại thành falsefavoriteNumber là một tính năng hoàn toàn khác: 0.5907281835659033.

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

Trong Quá trình làm việc dịch vụ chương 3, chúng ta hình dung trình chạy dịch vụ đã dừng của mình là bị mất toàn bộ màu sắc trong khi chờ đánh thức.

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

Cùng nhau, nhưng tách riêng

Chỉ một nhân viên dịch vụ có thể kiểm soát trang của bạn tại một thời điểm. Tuy nhiên, ứng dụng này có thể có 2 trình chạy dịch vụ được cài đặt cùng một lúc. Khi bạn thay đổi mã của trình chạy dịch vụ và làm mới trang, thực tế bạn sẽ không 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 danh sách hoàn toàn mới. Trình chạy dịch vụ mới này (hãy gọi là SW2) sẽ cài đặt nhưng chưa kích hoạt. 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 bộ nhớ đệm. Tuy nhiên, hãy lưu ý: trình chạy dịch vụ mới này có quyền truy cập vào mọi thứ mà trình chạy dịch vụ hiện tại có quyền truy cập vào. Nếu bạn không cẩn thận, nhân viên dịch vụ chờ mới của bạn có thể thực sự gây rối cho nhân viên dịch vụ hiện tại của bạn. Một số ví dụ có thể khiến bạn gặp rắc rối:

  • 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 bằng các thành phần mà trang không mong muốn.

Bỏ qua đang chờ

Nhân viên dịch vụ cũng có thể sử dụng phương thức skipWaiting() rủi ro để kiểm soát trang ngay sau khi cài đặt xong. Nói chung, đây là một ý tưởng không hay trừ phi bạn cố ý thay thế một worker dịch vụ bị lỗi. Có thể trình chạy dịch vụ mới đang sử dụng các tài nguyên đã 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 các trình chạy dịch vụ kết hợp lẫn 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 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ẽ đẩy version để trình chạy này thực hiện những gì cần thiết với bộ nhớ đệm hoàn toàn riêng biệt với trình chạy dịch vụ trước đó.

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

Kết thúc chế độ làm sạch

Sau khi trình chạy dịch vụ của bạn đạt đến trạng thái activated, bạn biết rằng trình chạy dịch vụ đó đã tiếp quản và trình chạy dịch vụ trước đó là không cần thiết. Tại thời điểm này, bạn cần phải dọn dẹp sau khi worker dịch vụ cũ. Điều này không chỉ tôn trọng hạn mức bộ nhớ đệm, nhưng cũng 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 trong bộ nhớ đệm của bất kỳ có kết quả trùng khớp. Tuy nhiên, công cụ này lặp lại qua các bộ nhớ đệm theo thứ tự tạo ra. Giả sử bạn có hai phiên bản của một tệp tập lệnh app.js trong hai bộ nhớ đệm khác nhau: assets-1assets-2. Trang của bạn đang mong đợ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ề dữ liệu cũ từ assets-1 và rất có thể sẽ phá vỡ trang web của bạn.

Tất cả những gì cần để dọn dẹp sau các 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);
          }
        });
      );
    });
  );
});

Việc ngăn chặn các nhân viên dịch vụ can thiệp lẫn nhau sẽ mất một chút công sức và kỷ luật nhưng rất xứng đáng.

Tư duy nhân viên phục vụ

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

Nếu muốn hiểu rõ 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! Chuyển đến phần Service Workies, nơi bạn sẽ tìm hiểu cách thức của worker dịch vụ để tiêu diệt những quái vật ngoại tuyến.