Trình chạy dịch vụ mới hơn, theo mặc định

tl;dr

Kể từ Chrome 68, theo mặc định, bộ nhớ đệm HTTP sẽ không thực hiện các yêu cầu HTTP kiểm tra bản cập nhật tập lệnh trình chạy dịch vụ nữa. Cách này giúp giải quyết vấn đề thường gặp của nhà phát triển, trong đó việc vô tình đặt tiêu đề Cache-Control vào tập lệnh trình chạy dịch vụ có thể khiến các bản cập nhật bị chậm trễ.

Nếu đã chọn không sử dụng tính năng lưu vào bộ nhớ đệm HTTP cho tập lệnh /service-worker.js bằng cách phân phát tập lệnh đó bằng Cache-Control: max-age=0, thì bạn sẽ không thấy bất kỳ thay đổi nào do hành vi mặc định mới.

Ngoài ra, kể từ Chrome 78, phép so sánh từng byte sẽ được áp dụng cho các tập lệnh được tải trong một trình chạy dịch vụ qua importScripts(). Mọi thay đổi đối với tập lệnh đã nhập sẽ kích hoạt quy trình cập nhật trình chạy dịch vụ, giống như một thay đổi đối với trình chạy dịch vụ cấp cao nhất.

Thông tin khái quát

Mỗi khi bạn chuyển đến một trang mới thuộc phạm vi của trình chạy dịch vụ, hãy gọi registration.update() một cách rõ ràng từ JavaScript hoặc khi trình chạy dịch vụ "đánh thức" thông qua sự kiện push hoặc sync, trình duyệt sẽ yêu cầu tài nguyên JavaScript ban đầu được chuyển vào lệnh gọi navigator.serviceWorker.register() để tìm nội dung cập nhật cho tập lệnh trình chạy dịch vụ.

Theo mục đích của bài viết này, giả sử URL của bài viết là /service-worker.js và chứa một lệnh gọi đến importScripts(). Lệnh gọi này sẽ tải mã bổ sung chạy bên trong trình chạy dịch vụ:

// Inside our /service-worker.js file:
importScripts('path/to/import.js');

// Other top-level code goes here.

Điều gì sẽ thay đổi?

Trước Chrome 68, yêu cầu cập nhật cho /service-worker.js sẽ được thực hiện qua bộ nhớ đệm HTTP (như hầu hết các lần tìm nạp đều thực hiện). Tức là nếu tập lệnh ban đầu được gửi bằng Cache-Control: max-age=600, thì các bản cập nhật trong vòng 600 giây (10 phút) tiếp theo sẽ không được kết nối mạng, vì vậy, người dùng có thể không nhận được phiên bản mới nhất của trình chạy dịch vụ. Tuy nhiên, nếu max-age lớn hơn 86400 (24 giờ), thì mã đó sẽ được xử lý như thể đó là 86400 để tránh việc người dùng bị mắc kẹt vĩnh viễn với một phiên bản cụ thể.

Kể từ phiên bản 68, bộ nhớ đệm HTTP sẽ bị bỏ qua khi yêu cầu cập nhật tập lệnh trình chạy dịch vụ. Vì vậy, các ứng dụng web hiện có có thể nhận thấy tần suất yêu cầu tập lệnh trình chạy dịch vụ tăng lên. Các yêu cầu cho importScripts sẽ vẫn chuyển qua bộ nhớ đệm HTTP. Tuy nhiên, đây chỉ là tuỳ chọn mặc định – có sẵn một tuỳ chọn đăng ký mới, updateViaCache cung cấp quyền kiểm soát hành vi này.

updateViaCache

Giờ đây, nhà phát triển có thể truyền một tuỳ chọn mới khi gọi navigator.serviceWorker.register(): tham số updateViaCache. Hàm này nhận một trong ba giá trị: 'imports', 'all' hoặc 'none'.

Các giá trị này xác định xem bộ nhớ đệm HTTP tiêu chuẩn của trình duyệt có hoạt động hay không và hoạt động như thế nào khi bạn gửi yêu cầu HTTP để kiểm tra các tài nguyên đã cập nhật của trình chạy dịch vụ.

  • Khi bạn đặt thành 'imports', bộ nhớ đệm HTTP sẽ không bao giờ được tham khảo khi kiểm tra bản cập nhật cho tập lệnh /service-worker.js, nhưng sẽ được tham khảo khi tìm nạp bất kỳ tập lệnh đã nhập nào (trong ví dụ là path/to/import.js). Đây là chế độ mặc định và khớp với hành vi bắt đầu từ Chrome 68.

  • Khi bạn đặt thành 'all', bộ nhớ đệm HTTP sẽ được tham khảo khi đưa ra yêu cầu cho cả tập lệnh /service-worker.js cấp cao nhất cũng như mọi tập lệnh được nhập bên trong trình chạy dịch vụ, chẳng hạn như path/to/import.js. Tuỳ chọn này tương ứng với hành vi trước đây trong Chrome, trước Chrome 68.

  • Khi bạn đặt thành 'none', bộ nhớ đệm HTTP sẽ không được tham khảo khi đưa ra yêu cầu cho /service-worker.js cấp cao nhất hoặc cho bất kỳ tập lệnh nào đã nhập, chẳng hạn như path/to/import.js giả định.

Ví dụ: Mã sau đây sẽ đăng ký một trình chạy dịch vụ và đảm bảo rằng bộ nhớ đệm HTTP không bao giờ được tham khảo khi kiểm tra bản cập nhật của tập lệnh /service-worker.js hoặc bất kỳ tập lệnh nào được tham chiếu qua importScripts() bên trong /service-worker.js:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js', {
    updateViaCache: 'none',
    // Optionally, set 'scope' here, if needed.
  });
}

Kiểm tra bản cập nhật cho các tập lệnh đã nhập

Trước Chrome 78, mọi tập lệnh của trình chạy dịch vụ được tải qua importScripts() sẽ chỉ được truy xuất một lần (trước tiên hãy kiểm tra bộ nhớ đệm HTTP hoặc qua mạng, tuỳ thuộc vào cấu hình updateViaCache). Sau lần truy xuất ban đầu đó, tệp sẽ được trình duyệt lưu trữ nội bộ và không bao giờ tìm nạp lại.

Cách duy nhất để buộc một trình chạy dịch vụ đã cài đặt nhận các thay đổi đối với tập lệnh đã nhập là thay đổi URL của tập lệnh, thường là bằng cách thêm giá trị bán kỳ (ví dụ: importScripts('https://example.com/v1.1.0/index.js')) hoặc bao gồm hàm băm của nội dung (ví dụ: importScripts('https://example.com/index.abcd1234.js')). Tác dụng phụ của việc thay đổi URL đã nhập là thay đổi nội dung của tập lệnh trình chạy dịch vụ cấp cao nhất, sau đó sẽ kích hoạt trình chạy dịch vụ cập nhật.

Kể từ Chrome 78, mỗi lần kiểm tra bản cập nhật cho tệp trình chạy dịch vụ cấp cao nhất, hệ thống sẽ cùng lúc kiểm tra để xác định xem nội dung của bất kỳ tập lệnh đã nhập nào có thay đổi hay không. Tuỳ thuộc vào tiêu đề Cache-Control được sử dụng, bộ nhớ đệm HTTP có thể thực hiện các hoạt động kiểm tra tập lệnh đã nhập này nếu updateViaCache được đặt thành 'all' hoặc 'imports' (đây là giá trị mặc định), hoặc các hoạt động kiểm tra đó có thể trực tiếp truy cập mạng nếu updateViaCache được đặt thành 'none'.

Nếu quá trình kiểm tra cập nhật cho một tập lệnh đã nhập dẫn đến sự chênh lệch theo từng byte so với những gì được trình chạy dịch vụ lưu trữ trước đó, thì điều đó sẽ kích hoạt quy trình cập nhật của trình chạy dịch vụ đầy đủ, ngay cả khi tệp trình chạy dịch vụ cấp cao nhất vẫn giữ nguyên.

Hành vi của Chrome 78 giống với hành vi mà Firefox đã triển khai vài năm trước, trong Firefox 56. Safari cũng đã triển khai hành vi này.

Nhà phát triển cần làm gì?

Nếu bạn đã chọn không sử dụng tính năng lưu vào bộ nhớ đệm HTTP cho tập lệnh /service-worker.js một cách hiệu quả bằng cách phân phát tập lệnh đó với Cache-Control: max-age=0 (hoặc một giá trị tương tự), thì bạn sẽ không thấy bất kỳ thay đổi nào do hành vi mặc định mới.

Nếu phân phát tập lệnh /service-worker.js có bật tính năng lưu vào bộ nhớ đệm HTTP, dù có chủ đích hoặc vì đó chỉ là chế độ mặc định cho môi trường lưu trữ, thì bạn có thể bắt đầu thấy số lượng yêu cầu HTTP bổ sung cho /service-worker.js được gửi đến máy chủ tăng lên. Đây là những yêu cầu mà bộ nhớ đệm HTTP cần thực hiện. Nếu muốn tiếp tục cho phép giá trị tiêu đề Cache-Control ảnh hưởng đến độ mới của /service-worker.js, bạn cần bắt đầu đặt updateViaCache: 'all' một cách rõ ràng khi đăng ký trình chạy dịch vụ.

Do có thể vẫn còn người dùng trên các phiên bản trình duyệt cũ, bạn vẫn nên tiếp tục đặt tiêu đề HTTP Cache-Control: max-age=0 trên các tập lệnh trình chạy dịch vụ, mặc dù các trình duyệt mới hơn có thể bỏ qua tiêu đề này.

Nhà phát triển có thể tận dụng cơ hội này để quyết định xem họ có muốn chọn một cách rõ ràng các tập lệnh đã nhập không sử dụng chức năng lưu vào bộ nhớ đệm HTTP ngay bây giờ hay không và thêm updateViaCache: 'none' vào quy trình đăng ký trình chạy dịch vụ (nếu phù hợp).

Phân phối tập lệnh đã nhập

Kể từ Chrome 78, nhà phát triển có thể nhận thấy nhiều yêu cầu HTTP hơn đến cho các tài nguyên được tải qua importScripts(), vì các tài nguyên này từ nay sẽ được kiểm tra để tìm bản cập nhật.

Nếu bạn muốn tránh lưu lượng truy cập HTTP bổ sung này, hãy thiết lập các tiêu đề Cache-Control dài hạn khi phân phát các tập lệnh có chứa semver hoặc hàm băm trong URL, đồng thời dựa vào hành vi updateViaCache mặc định của 'imports'.

Ngoài ra, nếu bạn muốn các tập lệnh đã nhập được kiểm tra để xem có bản cập nhật thường xuyên hay không, hãy đảm bảo bạn phân phát các tập lệnh đó bằng Cache-Control: max-age=0 hoặc bạn sử dụng updateViaCache: 'none'.

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

Tất cả những nhà phát triển triển khai bất cứ thứ gì trên web đều nên đọc các bài viết về "Vòng đời của trình chạy dịch vụ" và "Các phương pháp hay nhất về việc lưu vào bộ nhớ đệm và các lỗi thường gặp ở thời điểm tối đa" – cả hai đều do Jake Archibald đề xuất.