★AppGiaiTri Đăng nhập
Công nghệ đằng sau

Service worker: người giữ kho giúp ứng dụng web chạy khi mất mạng, và vì sao đôi khi kẹt bản cũ

Vòng đời cài đặt, kích hoạt, trực cửa; ba chiến lược cho ba loại yêu cầu; chuỗi ba bước đưa bản mới tới tay người dùng, lỗi quên tăng số phiên bản và cách tự thoát khỏi bản cũ trên điện thoại.

Service worker: người giữ kho giúp ứng dụng web chạy khi mất mạng, và vì sao đôi khi kẹt bản cũ

Mở một ứng dụng web trên máy bay ở chế độ không mạng, và nó vẫn chạy như thường. Với nhiều người đó là điều bất ngờ: trang web mà không cần mạng? Thứ làm được việc đó là một tệp nhỏ có tên service worker. Bài này gọi nó là “người giữ kho” — vì đúng là nó làm việc đó — và giải thích cách nó làm việc, những quy tắc nó tuân theo, và vì sao cũng chính nó đôi khi khiến bạn kẹt lại ở bản cũ dù người làm đã sửa lỗi từ hôm qua. Ví dụ lấy từ tệp service worker thật của các ứng dụng trong AppGiaiTri.

Người giữ kho đứng ở đâu

Bình thường, mỗi khi trang web cần một thứ — tệp mã, hình ảnh, biểu tượng — trình duyệt gửi yêu cầu thẳng lên máy chủ. Service worker chen vào giữa: nó là một đoạn mã chạy riêng, đứng chắn trước cửa và nhìn thấy mọi yêu cầu của trang trước khi chúng ra ngoài. Với từng yêu cầu, nó được quyền quyết định: đi lấy trên mạng, lấy trong kho của mình, hay kết hợp cả hai.

Ba điều cần biết về người giữ kho này:

  • Nó sống lâu hơn trang. Đóng tab rồi mở lại, nó vẫn ở đó, vẫn nhớ kho của mình.
  • Nó chỉ trông coi đúng một địa chỉ. Service worker của Giao Đấu không nhìn thấy gì của Rượt Đuổi, dù hai ứng dụng nằm cạnh nhau trên màn hình chính.
  • Nó chỉ chạy trên kết nối có mã hoá. Vì đứng chắn mọi yêu cầu, một service worker giả mạo sẽ cực kỳ nguy hiểm; trình duyệt chỉ cho phép nó trên địa chỉ có khoá bảo mật.

Vòng đời: nhận việc, bàn giao, trực cửa

Người giữ kho không bắt đầu làm việc ngay khi được thuê. Nó đi qua ba giai đoạn, và hiểu ba giai đoạn này là hiểu được mọi chuyện “kẹt bản cũ” về sau.

  1. Cài đặt. Lần đầu mở ứng dụng khi có mạng, trình duyệt tải tệp service worker về và cho nó chạy phần “nhận việc”. Ở các ứng dụng AppGiaiTri, phần này mở một ngăn kho mang tên phiên bản (kiểu giaodau-v6) và cất vào đó danh sách tệp cốt lõi: trang chính, tệp mã, tệp giao diện, bộ công cụ dùng chung, biểu tượng. Chỉ cần một tệp trong danh sách tải hỏng, cả lần cài bị huỷ — thà không có kho còn hơn kho thiếu.
  2. Kích hoạt. Khi đã cài xong, người giữ kho mới dọn dẹp: mọi ngăn kho mang tên phiên bản khác bị xoá. Đây là chỗ bộ nhớ cũ được giải phóng.
  3. Trực cửa. Từ đây, mọi yêu cầu của trang đều đi qua nó.

Có một quy tắc mặc định rất quan trọng: nếu đang có một người giữ kho cũ trực cửa, người mới cài xong sẽ phải đứng chờ cho tới khi mọi tab, mọi cửa sổ của ứng dụng đó đóng hết. Trình duyệt làm vậy để một trang đang chạy không bị đổi luật giữa chừng. Người làm có thể cho người mới “vào ca ngay” — các ứng dụng AppGiaiTri làm vậy — nhưng trang đang mở vẫn đang dùng những tệp đã nạp từ trước, nên người dùng chỉ thấy bản mới ở lần mở kế tiếp.

Mỗi loại yêu cầu một cách phục vụ

Không phải thứ gì cũng nên lấy từ kho. Tệp service worker của AppGiaiTri chia yêu cầu làm ba loại, mỗi loại một chiến lược:

Loại yêu cầuChiến lượcVì sao
Mở trang (trang chính của ứng dụng)Hỏi mạng trước; mạng hỏng mới lấy bản trong khoCó mạng thì luôn nhận trang mới nhất; bản lấy được còn được cất lại vào kho cho lần mất mạng sau
Tệp mã, giao diện, hình ảnhLấy trong kho trước; kho không có mới hỏi mạngMở tức thì, không tốn dữ liệu di động cho thứ đã có
Mọi thứ liên quan tài khoản, lượt chơi, bảng xếp hạngKhông bao giờ đụng vào kho, luôn đi thẳng lên mạngDữ liệu cá nhân và kết quả phải luôn mới, và không được nằm lại trên máy dưới dạng bản sao cũ

Hàng thứ ba đáng nhấn mạnh. Một lỗi kinh điển của người mới làm là cho service worker cất mọi thứ, kể cả câu trả lời của máy chủ về “tôi là ai”. Kết quả: đăng xuất rồi mà ứng dụng vẫn hiện tên cũ, hoặc tệ hơn, trên máy dùng chung, người sau thấy thông tin của người trước. Luật an toàn là liệt kê rõ những gì được cất, còn lại để đi mạng.

Còn khi mất mạng mà cần gửi kết quả lượt chơi? Ứng dụng vẫn cho chơi bình thường, kết quả được ghi nhận trên máy, chỉ là lượt đó không lên bảng xếp hạng. Màn hình kết thúc ghi thẳng “Chưa đồng bộ — sẽ giữ trên máy”, thay vì giả vờ “sẽ gửi sau” rồi âm thầm thất bại.

Vì sao đôi khi kẹt ở bản cũ

Đây là phần mà người làm ứng dụng web nào cũng từng mất ngủ ít nhất một lần. Chiến lược “lấy trong kho trước” cho tốc độ tuyệt vời, nhưng nó có một mặt trái: nếu kho đã có tệp tên main.js, người giữ kho sẽ đưa đúng tệp đó ra mãi mãi, bất kể trên máy chủ nó đã được sửa bao nhiêu lần.

Thế thì bản mới đến tay người dùng bằng cách nào? Bằng một chuỗi ba bước phải khớp nhau hoàn toàn:

  1. Tên tệp phải đổi. Mỗi tệp mã được gọi kèm một số phiên bản ở cuối địa chỉ (kiểu main.js?v=6). Sửa mã thì tăng số lên 7. Với người giữ kho, đó là một tệp hoàn toàn mới, không có trong kho, phải đi lấy trên mạng.
  2. Tệp service worker phải khác đi dù chỉ một chữ. Mỗi lần mở ứng dụng có mạng, trình duyệt tải lại tệp service worker và so từng byte với bản đang chạy. Giống hệt thì không làm gì. Khác một byte thì bắt đầu vòng đời cài đặt mới. Vì vậy tên ngăn kho (giaodau-v6 thành giaodau-v7) phải đổi theo.
  3. Trang chính phải trỏ tới tên mới. Trang chính đi mạng trước, nên nó luôn mới; nó liệt kê main.js?v=7 thì trình duyệt sẽ xin đúng bản đó.

Quên bất kỳ bước nào là kẹt. Kiểu kẹt phổ biến nhất: người làm sửa mã, tải lên máy chủ, thử trên máy mình (đã xoá kho từ trước) thấy chạy tốt — trong khi mọi người dùng cũ vẫn nhận bản cũ từ kho, vì số phiên bản không đổi. Không có thông báo lỗi nào, chỉ có người dùng báo “vẫn bị như cũ” và người làm thì khẳng định “sửa rồi mà”.

AppGiaiTri xử lý bằng cách không để con người nhớ việc này. Có một lệnh tăng phiên bản chạy trước mỗi lần đưa bản mới lên: nó tăng số ở cuối mọi địa chỉ tệp, trong trang chính và trong tệp service worker, cùng một lúc. Bộ công cụ dùng chung của cả năm ứng dụng cũng mang số phiên bản riêng; đổi nó thì cả năm ứng dụng cùng được tăng số. Quy tắc trong sổ tay nội bộ ghi rõ: đổi mã mà không tăng số thì máy đã từng mở ứng dụng sẽ chạy bản cũ.

Những trường hợp vẫn thấy bản cũ dù người làm đã làm đúng

  • Ứng dụng trên màn hình chính chưa từng thật sự đóng. Vuốt về màn hình chính không phải là đóng; ứng dụng vẫn nằm trong bộ nhớ với tệp cũ đã nạp. Phải vuốt nó khỏi trình chuyển ứng dụng rồi mở lại.
  • Mở lúc mất mạng. Không có mạng thì không có gì để so, người giữ kho phục vụ bản trong kho — đúng như thiết kế.
  • Mới mở lại ngay sau khi có bản mới. Lần mở đó trình duyệt mới phát hiện và cài bản mới ở chế độ nền; có khi phải mở thêm một lần nữa.

Có một nguyên tắc nữa trong bản thiết kế của AppGiaiTri: không bao giờ tự tải lại trang giữa trận để áp bản mới. Bản mới có thể đợi tới lần mở sau; một pha bóng đang dở thì không đợi được.

Kho này tốn bao nhiêu chỗ?

Ít hơn bạn nghĩ. Mỗi ứng dụng chỉ cất đúng danh sách tệp cốt lõi của mình — không hình nền khổng lồ, không tệp âm thanh (vì âm thanh được tạo bằng mã ngay trên máy). Mỗi lần có phiên bản mới, ngăn kho cũ bị xoá sạch ở bước kích hoạt, nên kho không phình dần theo thời gian. Trên iPhone, trình duyệt có thể dọn dữ liệu của trang không được mở lại một thời gian; ứng dụng đã thêm vào màn hình chính thì được đối xử như ứng dụng thật và ít bị dọn hơn. Dù sao, thứ gì quan trọng như tiến độ đều nên nằm trên tài khoản chứ không chỉ trong kho của máy.

Câu hỏi thường gặp

Service worker là gì?

Service worker là một đoạn mã chạy nền do trang web đăng ký, đứng giữa trang và mạng để quyết định mỗi yêu cầu lấy từ mạng hay từ kho lưu sẵn trên máy. Nhờ nó, ứng dụng web mở được khi mất mạng và mở nhanh hơn ở những lần sau.

Vì sao ứng dụng web không cập nhật bản mới trên điện thoại của tôi?

Thường vì ứng dụng vẫn đang chạy với tệp cũ đã nạp, hoặc bạn mở lúc không có mạng. Hãy đóng hẳn ứng dụng khỏi trình chuyển ứng dụng rồi mở lại khi có mạng, có khi cần mở lại lần thứ hai. Nếu vẫn không được, lỗi thường nằm ở phía người làm quên tăng số phiên bản.

Ứng dụng web chạy offline có cần tải gì trước không?

Chỉ cần mở ứng dụng một lần khi có mạng. Lần đó service worker tự cất sẵn các tệp cần thiết; từ lần sau ứng dụng mở được cả khi không có sóng.

Chơi khi mất mạng thì kết quả có được lưu không?

Kết quả và tiến độ vẫn được ghi trên máy. Riêng các lượt chơi lúc mất mạng không lên bảng xếp hạng, vì máy chủ không thể xác nhận lượt đó từ đầu.

Xoá dữ liệu trang web có làm mất tiến độ không?

Xoá dữ liệu sẽ xoá cả kho của service worker và mọi thứ chỉ lưu trên máy. Nếu bạn đã đăng nhập, tiến độ trên tài khoản vẫn còn và sẽ được tải lại khi mở ứng dụng có mạng.

Service worker có đọc được dữ liệu của ứng dụng khác không?

Không. Mỗi service worker chỉ trông coi đúng địa chỉ trang đã đăng ký nó, và chỉ chạy trên kết nối có mã hoá. Nó không nhìn thấy yêu cầu của trang khác hay dữ liệu của ứng dụng khác trên máy.

Cần tư vấn cụ thể cho trường hợp của bạn?

Chúng tôi sẽ liên hệ trong vòng 24 giờ.

Đăng ký tư vấn ngay

Bài viết liên quan

Bạn cần hỗ trợ thêm?

Để lại thông tin, chúng tôi sẽ liên hệ trong 24 giờ.