Tăng tốc độ tải trang với Progressive rendering
Tốc độ tải trang web ảnh hưởng rất nhiều tới khách truy cập, website chậm đi sẽ làm giảm trải nghiệm người dùng và làm giảm hiệu quả của trang web. Có nhiều phương pháp để làm giảm tốc độ tải trang như:
- Tối ưu, giảm dung lượng truyền tải giữa serverside và browser
- Bật nén (Gzip)
- Sử dụng Browser caching
- Nén các tài nguyên cần thiết
- Nén ảnh trên web
- Sử dụng CDN
Các phương pháp trên tuy rất đơn giản nhưng mang lại hiệu quả rất lớn trong việc tối ưu tốc độ tải của trang web. Hôm nay Bizfly Cloud sẽ giới thiệu một phương pháp "không mới" nhưng dường như đã bị lãng quên từ lâu, đó là sử dụng kỹ thuật Progressive Rending để làm giảm thời gian tải trang "thật", và thời gian tải trang "cảm nhận được". Trước khi bắt đầu thì mình có một số quy ước như sau:
Thời gian tải trang thật
Là thời gian thật mà trình duyệt cần thiết để có thể tải và render toàn bộ nội dung, bao gồm html page, resource (js, css, fonts...)
Thời gian tải trang cảm nhận được
Là thời gian mà người sử dụng website cảm nhận về việc tải trang, yếu tố này mang tính chủ quan của từng người truy cập và rất khó có thể đưa ra một cách chính xác để tính toán thời gian này.
"Thời gian tải trang cảm nhận được" có giá trị quan trọng không kém "Thời gian tải trang thật" bởi vì nó được phản ánh bởi nhận thức của người dùng. Nhận thức này thể hiện ở việc người dùng nhìn thấy toàn bộ hoặc một phần website hiển thị ra trong quá trình load.
![Tăng tốc độ tải trang với Progressive rendering - Ảnh 1. Thời gian tải trang cảm nhận được - 1](https://techvccloud.mediacdn.vn/thumb_w/650/280518386289090560/2024/1/8/photo-1-15278464479821304175603-1704708960074458699810.jpg)
Thử tưởng tượng bạn có một tác vụ cần xử lý trong một khoảng thời gian đủ dài, và người dùng phải chờ đợi để tác vụ của bạn hòan thành trước khi có thể thấy toàn bộ nội dung. Vấn đề đặt ra ở đây đó là đối với các website, phần lớn sẽ tồn tại những phần thành phần chung như Menu, Sidebar mà sẽ được cache hoặc có thời gian để lấy ra rất nhanh. Nếu có thể hiển thị trước các thành phần này thì sẽ giúp cung cấp cho người dùng tín hiệu rằng: "Chúng tôi vẫn đang tiếp tục xử lý công việc, đây là một số thứ chúng tôi đã xử lý xong, tuy nhiên vẫn còn nữa :D (loading indicator vẫn hiển thị)".
Đồng thời sẽ tạo cho người dùng cảm giác rằng "Nó vẫn load được; web hiển thị được một phần rồi, chắc sắp xong..."
![Tăng tốc độ tải trang với Progressive rendering - Ảnh 2. Thời gian tải trang cảm nhận được - 2](https://techvccloud.mediacdn.vn/280518386289090560/2024/1/8/anh-1-17047091702271810925340.jpg)
Sử dụng Progressive rendering
![Tăng tốc độ tải trang với Progressive rendering - Ảnh 3. Thời gian tải trang cảm nhận được - 3](https://techvccloud.mediacdn.vn/280518386289090560/2024/1/8/anh-2-17047091862662027143112.jpg)
Để có cái nhìn rõ ràng hơn về Progressvie rendering, mình sẽ mô tả thêm một chút về cơ chế của nó.
(1) Không có Progressive rendering
: Chờ cho tất cả các tác vụ hoàn thành xong, gom các output lại thành một string rồi response lại cho phía client.
(2) Có Progressive rendering
: Mỗi tác vụ chạy xong sẽ có output, ngay lập tức đẩy (stream) ouput này xuống client cho trình duyệt xử lý
Ngoài ưu điểm là đánh vào cảm nhận của người dùng thì Progressive rendering có tác dụng gì đối với thời gian tải trang thực không nhỉ?
Loading indicator:![Tăng tốc độ tải trang với Progressive rendering - Ảnh 1. Tăng tốc độ tải trang với Progressive rendering - Ảnh 1.](https://techvccloud.mediacdn.vn/2018/6/1/photo-3-1527844368825366376819.gif)
Như các bạn đã biết, loading indicator sẽ chỉ biến mất (thay thế bởi favicon) khi tất cả các tài nguyên cần thiết của website đã được load (css, js, image, iframe).
Trình duyệt sẽ bắt đầu tải các tài nguyên này khi nó thực hiện render mã html nhận được và bắt gặp markup yêu cầu sử dụng tài nguyên bên ngoài (<script src="..."></script>,<link href="..."/>,<iframe src="..."><iframe>)
- Nếu làm theo (1) thì trình tự xử lý sẽ như sau: All server side tasks completed
-----> response
-----> browser rendering
-----> load all html resource
- Còn đồi với (2) thì trình tự sẽ như sau:
service side task 1 completed
-----> to response
-----> browser rendering
-----> load task 1 output resource
service side task 2 completed
-----> to response
-----> browser rendering
-----> load task 2 output resource
service side task 3 completed
-----> to response
-----> browser rendering
-----> load task 3 output resource
...
Thông thường, để đảm bảo các tài nguyên cần thiết được load trước khi html content được render, chúng ta thường "nhét" hết các file css, fonts, hoặc thậm chí cả js lên trên header (thẻ head) của website; mà thẻ này lại là thành phần chung nhỉ.
>>> Có thể bạn quan tâm: 5 công cụ kiểm tra Website Speed Test miễn phí
Do đó ta có thể tách việc output ra header thành một task riêng, nó chỉ có nhiệm vụ là tạo ra mã html của thẻ head rồi stream lại cho trình duyệt. Sau đó trong lúc server side của ta đang bận bịu với việc khác (query db, xử lý logic) thì đồng thời phía client side cũng đã nhận được mã html của header và bắt đầu tải luôn các resource cần thiết mà không cần chờ cho phía server side phải hoàn tất.
Cụ thể việc so sánh này sẽ được minh họa bởi các hình dưới đây:
![Tăng tốc độ tải trang với Progressive rendering - Ảnh 2. Tăng tốc độ tải trang với Progressive rendering - Ảnh 2.](https://techvccloud.mediacdn.vn/2018/6/1/photo-4-1527844368826537717070.png)
![Tăng tốc độ tải trang với Progressive rendering - Ảnh 3. Tăng tốc độ tải trang với Progressive rendering - Ảnh 3.](https://techvccloud.mediacdn.vn/2018/6/1/photo-5-1527844368829308446749.png)
Html ngay từ ban đầu đã được thiết kế để có thể được render dần dần từng mảnh một mỗi khi nhận được data từ server side. Do đó việc mỗi task lại write vào response một cách riêng rẽ sẽ không gây vấn đề gì cả. Mình dùng từ response trong bài khá nhiều nhưng nó không có nghĩa rằng mỗi task lại có một response riêng về client side, nó chỉ có nghĩa rằng tất cả các task sẽ write vào một response duy nhất về client, và chuỗi response đó sẽ chỉ dừng lại khi task cuối cùng chủ động làm công việc terminate cái response đó. Progressive loading không phải là một kỹ thuật mới, nó là một kỹ thuật đã quá cũ rồi.
Cho đến nay thì Progressive rendering vẫn phát huy rất nhiều giá trị, ông lớn FB cũng đang sử dụng kỹ thuật này (giữ kết nối và đẩy dữ liệu thành từng phần) như là một phần của Bigpipe (một ứng dụng giúp tối ưu việc sử lý và response dữ liệu về client side).
Nếu bạn quan tâm và muốn implement Progressive rendering thì có thể tìm hiểu thông qua các gợi ý sau:
- PHP
: Sử dụng flush
và chunked encoding.
- Node.js
: Sử dụng res.write
để ghi dữ liệu vào response hoặc Markojs
để stream từ Template Engine.
- Các ngôn ngữ khác.
Liên quan đến Progressive rendering còn một kỹ thuật nữa để hỗ trợ tối ưu page loading đó là Multi pipes
; nó sử dụng Progressive rendering
và khác với BigPipe
của Facebook ở chỗ nó cần chia nhỏ một request lớn thành nhiều request nhỏ, việc còn lại là nhận response và mapping response đúng với vị trí cần thiết sẽ được Multi pipes
đảm nhiệm.
Các bạn có thể hình dung về Multi pipes thông qua kỹ thuật multiple flushes
được sử dụng trong php
để làm Progressive rendering.
Nguồn tham khảo:
http://www.phpied.com/progressive-rendering-via-multiple-flushes/
https://github.com/marko-js-samples/marko-progressive-rendering
https://marko-progressive-rendering.herokuapp.com/?renderMode=single-chunk&jsLocation=middle
http://www.phpied.com/progressive-rendering-via-multiple-flushes/
https://blog.codinghorror.com/the-lost-art-of-progressive-html-rendering/
http://www.ebaytechblog.com/2014/12/08/async-fragments-rediscovering-progressive
html-rendering-with-marko/
>> Có thể bạn quan tâm: 6 tips dễ thực hiện nhất giúp nâng cao hiệu suất Website
Theo viblo.asia