Truyền trực tuyến theo cách của bạn đến câu trả lời tức thì

Jeff Posnick
J Jeff Posnick

Bất cứ ai đã sử dụng service worker đều có thể cho bạn biết rằng chúng không đồng bộ hoàn toàn. Các API này chỉ dựa vào giao diện dựa trên sự kiện, chẳng hạn như FetchEvent và sử dụng hứa hẹn để ra tín hiệu khi các hoạt động không đồng bộ hoàn tất.

Tính không đồng bộ cũng quan trọng không kém, mặc dù nhà phát triển khó thấy hơn, khi nói đến phản hồi do trình xử lý sự kiện tìm nạp của trình chạy dịch vụ cung cấp. Phản hồi truyền trực tuyến là tiêu chuẩn vàng ở đây: chúng cho phép trang đưa ra yêu cầu ban đầu bắt đầu xử lý phản hồi ngay khi có phần dữ liệu đầu tiên. Ngoài ra, bạn có thể sử dụng các trình phân tích cú pháp được tối ưu hoá để truyền trực tuyến để hiển thị dần nội dung.

Khi viết trình xử lý sự kiện fetch của riêng mình, thông thường, bạn chỉ cần truyền phương thức respondWith() một Response (hoặc lời hứa cho Response) mà bạn nhận được qua fetch() hoặc caches.match() và gọi phương thức đó là một ngày. Tin vui là Response do cả hai phương thức đó tạo ra hiện đã có thể phát trực tuyến! Tuy nhiên, tin xấu là các Response được tạo "theo cách thủ công" không thể phát trực tuyến, ít nhất là cho đến bây giờ. Đó là nơi API Luồng (Streams API) nhập hình ảnh.

Lượt phát trực tuyến?

Luồng là một nguồn dữ liệu có thể được tạo và chỉnh sửa từ từ, đồng thời cung cấp một giao diện để đọc hoặc ghi các phần dữ liệu không đồng bộ, chỉ một tập con trong số đó có thể có sẵn trong bộ nhớ tại bất kỳ thời điểm nào. Hiện tại, chúng ta đang quan tâm đến ReadableStream, có thể dùng để tạo đối tượng Response được truyền đến fetchEvent.respondWith():

self.addEventListener('fetch', event => {
    var stream = new ReadableStream({
    start(controller) {
        if (/* there's more data */) {
        controller.enqueue(/* your data here */);
        } else {
        controller.close();
        }
    });
    });

    var response = new Response(stream, {
    headers: {'content-type': /* your content-type here */}
    });

    event.respondWith(response);
});

Trang có yêu cầu đã kích hoạt sự kiện fetch sẽ nhận lại phản hồi truyền trực tuyến ngay khi event.respondWith() được gọi và sẽ tiếp tục đọc từ luồng đó miễn là trình chạy dịch vụ tiếp tục enqueue()dữ liệu bổ sung. Phản hồi chuyển từ trình chạy dịch vụ đến trang thực sự không đồng bộ và chúng tôi có toàn quyền kiểm soát việc điền vào luồng!

Công dụng trong thực tế

Có thể bạn đã nhận thấy ví dụ trước có một số nhận xét về phần giữ chỗ /* your data here */, đồng thời tập trung vào các chi tiết triển khai thực tế. Vậy một ví dụ thực tế trông như thế nào?

Jake Archibald (không ngạc nhiên!) có một ví dụ tuyệt vời về việc sử dụng luồng để ghép một phản hồi HTML với nhau từ nhiều đoạn mã HTML đã lưu vào bộ nhớ đệm, cùng với dữ liệu "trực tiếp" được truyền qua fetch() — trong trường hợp này là nội dung cho blog của ông

Lợi thế của việc sử dụng phản hồi truyền trực tuyến, như Jake giải thích, là trình duyệt có thể phân tích cú pháp và hiển thị HTML khi luồng vào, bao gồm cả bit đầu tiên được tải nhanh từ bộ nhớ đệm mà không phải đợi toàn bộ quá trình tìm nạp nội dung blog hoàn tất. Điều này tận dụng tối đa khả năng kết xuất HTML tiến bộ của trình duyệt. Các tài nguyên khác cũng có thể hiển thị dần (như một số định dạng hình ảnh và video) cũng có thể hưởng lợi từ phương pháp này.

Lượt phát trực tuyến? Hay ứng dụng shell?

Các phương pháp hay nhất hiện có về việc sử dụng trình chạy dịch vụ để tăng cường sức mạnh cho ứng dụng web của bạn tập trung vào mô hình App Shell + nội dung động. Phương pháp đó dựa trên việc lưu trữ nghiêm ngặt "lớp" của ứng dụng web vào bộ nhớ đệm (HTML, JavaScript và CSS tối thiểu cần thiết để hiển thị cấu trúc và bố cục của bạn), sau đó tải nội dung động cần thiết cho từng trang cụ thể thông qua yêu cầu phía máy khách.

Các luồng truyền phát mang đến một giải pháp thay thế cho mô hình App Shell, trong đó có phản hồi HTML đầy đủ hơn được truyền đến trình duyệt khi người dùng chuyển đến một trang mới. Phản hồi được truyền trực tuyến có thể tận dụng tài nguyên đã lưu vào bộ nhớ đệm – vì vậy, phản hồi này vẫn có thể cung cấp đoạn HTML ban đầu một cách nhanh chóng, ngay cả khi không có mạng! – nhưng cuối cùng chúng sẽ trông giống như các nội dung phản hồi truyền thống do máy chủ kết xuất. Ví dụ: nếu ứng dụng web của bạn được hệ thống quản lý nội dung hỗ trợ mà máy chủ kết xuất HTML bằng cách ghép các mẫu một phần lại với nhau, thì mô hình đó sẽ trực tiếp chuyển sang sử dụng phản hồi truyền trực tuyến, với logic lập mẫu được sao chép trong trình chạy dịch vụ thay vì máy chủ của bạn. Video sau đây minh hoạ cho trường hợp sử dụng đó, lợi thế về tốc độ mà phản hồi truyền trực tuyến mang lại có thể rất ấn tượng:

Một lợi thế quan trọng của việc truyền trực tuyến toàn bộ phản hồi HTML (giải thích lý do tại sao đây là giải pháp thay thế nhanh nhất trong video) là HTML hiển thị trong yêu cầu điều hướng ban đầu có thể tận dụng tối đa trình phân tích cú pháp HTML truyền trực tuyến của trình duyệt. Các đoạn HTML được chèn vào tài liệu sau khi trang đã tải (như phổ biến trong mô hình App Shell) không thể tận dụng tính năng tối ưu hoá này.

Vậy nếu đang trong giai đoạn lập kế hoạch triển khai trình chạy dịch vụ, bạn nên áp dụng mô hình nào: phản hồi truyền trực tuyến được hiển thị dần dần hay shell gọn nhẹ cùng với yêu cầu phía máy khách cho nội dung động? Không ngạc nhiên khi điều này phụ thuộc vào việc bạn có đang triển khai dựa trên CMS hay một phần mẫu (ưu điểm: stream) hay không; tuỳ thuộc vào việc bạn có mong đợi các tải trọng HTML lớn và đơn lẻ sẽ được hưởng lợi từ việc hiển thị tiến trình (ưu điểm: stream); về việc ứng dụng web của bạn có được lập mô hình tốt nhất dưới dạng ứng dụng trang đơn (ưu điểm: Ứng dụng Shell); và liệu bạn có cần một mô hình hiện được hỗ trợ ổn định hay không:

Chúng tôi vẫn ở những giai đoạn đầu của việc phản hồi truyền trực tuyến dựa trên trình chạy dịch vụ. Chúng tôi mong muốn thấy các mô hình khác nhau hoàn thiện hơn, đặc biệt là có thêm nhiều công cụ được phát triển để tự động hoá các trường hợp sử dụng phổ biến.

Tìm hiểu sâu hơn về các luồng

Nếu bạn đang xây dựng các luồng có thể đọc của riêng mình, việc chỉ gọi controller.enqueue() một cách bừa bãi có thể là không đủ hoặc hiệu quả. Jake sẽ đi sâu vào một số thông tin chi tiết về cách sử dụng song song các phương thức start(), pull()cancel() để tạo luồng dữ liệu phù hợp với trường hợp sử dụng của bạn.

Đối với những ai muốn biết thêm thông tin chi tiết, vui lòng tham khảo Thông số kỹ thuật của Luồng.

Khả năng tương thích

Hỗ trợ việc tạo đối tượng Response bên trong một trình chạy dịch vụ bằng cách sử dụng ReadableStream làm nguồn đã được thêm vào Chrome 52.

Quá trình triển khai trình chạy dịch vụ của Firefox chưa hỗ trợ phản hồi được ReadableStream hỗ trợ, nhưng bạn có thể theo dõi một lỗi theo dõi liên quan đến việc hỗ trợ API luồng.

Bạn có thể theo dõi tiến trình hỗ trợ API Streams chưa có tiền tố trong Edge, cùng với hỗ trợ trình chạy dịch vụ tổng thể tại trang Trạng thái nền tảng của Microsoft.