Web Performance Made dễ dàng – Google I/O phiên bản 2018

Tại Google IO 2018, chúng tôi đã giới thiệu một bản tổng hợp về các công cụ, thư viện và kỹ thuật tối ưu hóa giúp cải thiện hiệu suất web dễ dàng hơn. Ở đây, chúng tôi sẽ giải thích về các định dạng này thông qua ứng dụng Oodles rạp hát. Đồng thời, chúng tôi cũng sẽ trình bày các thử nghiệm liên quan đến tính năng tải theo dự đoán và sáng kiến Answer.js mới.

Addy Osmani
Addy Osmani
Ewa Gasperowicz

Chúng tôi khá bận rộn trong năm qua để tìm cách giúp Web nhanh hơn và hiệu quả hơn. Điều này đã dẫn đến các công cụ, phương pháp và thư viện mới mà chúng tôi muốn chia sẻ với bạn trong bài viết này. Trong phần đầu tiên, chúng tôi sẽ giới thiệu cho bạn một số kỹ thuật tối ưu hoá mà chúng tôi đã sử dụng trong thực tế khi phát triển ứng dụng Oodles Theater. Trong phần thứ hai, chúng ta sẽ thảo luận về các thử nghiệm của chúng tôi với tính năng tải trước và sáng kiến Guess.js mới.

Nhu cầu về hiệu suất

Internet ngày càng nặng hơn mỗi năm. Nếu kiểm tra trạng thái web, chúng tôi có thể thấy rằng một trang trung bình trên thiết bị di động có trọng số vào khoảng 1,5 MB, trong đó phần lớn là JavaScript và hình ảnh.

Kích thước ngày càng lớn của các trang web, cùng với các yếu tố khác như độ trễ mạng, giới hạn CPU, mẫu chặn kết xuất hoặc mã bên thứ ba thừa, góp phần tạo nên một câu đố hiệu suất phức tạp.

Hầu hết người dùng đều đánh giá tốc độ là ưu tiên hàng đầu trong hệ phân cấp trải nghiệm người dùng theo nhu cầu của họ. Điều này không quá đáng ngạc nhiên vì bạn không thể thực sự làm được nhiều việc cho đến khi trang tải xong. Bạn không thể lấy giá trị từ trang, không thể chiêm ngưỡng tính thẩm mỹ của trang.

Biểu đồ phân cấp trải nghiệm người dùng
Hình 1. Tốc độ có quan trọng với người dùng không? (Vấn đề tốc độ, Tập 3)

Chúng tôi biết rằng hiệu suất rất quan trọng đối với người dùng, nhưng cũng có thể khó khăn khi tìm ra nơi bắt đầu tối ưu hoá. May mắn là có những công cụ có thể giúp bạn trong quá trình thực hiện.

Lighthouse – cơ sở cho quy trình làm việc về hiệu suất

Lighthouse là một phần của Công cụ của Chrome cho nhà phát triển, cho phép bạn kiểm tra trang web của mình và đưa ra gợi ý về cách cải thiện trang web.

Gần đây, chúng tôi đã ra mắt một loạt quy trình kiểm tra hiệu suất mới rất hữu ích trong quy trình phát triển hằng ngày.

Kiểm tra Lighthouse mới
Hình 2. Các bài kiểm tra mới của Lighthouse

Hãy cùng tìm hiểu cách khai thác tối đa chúng qua một ví dụ thực tế: Ứng dụng Oodles rạp. Đây là một ứng dụng web minh hoạ nhỏ, nơi bạn có thể dùng thử một số Hình tượng trưng Google có tính tương tác mà chúng tôi yêu thích và thậm chí chơi một hoặc hai trò chơi.

Trong quá trình tạo ứng dụng, chúng tôi muốn đảm bảo ứng dụng hoạt động hiệu quả nhất có thể. Điểm xuất phát để tối ưu hoá là một báo cáo Lighthouse.

Báo cáo Lighthouse cho ứng dụng Oodles
Hình 3. Báo cáo Lighthouse cho ứng dụng Oodles

Hiệu suất ban đầu của ứng dụng như trong báo cáo Lighthouse khá tệ. Trên mạng 3G, người dùng cần đợi 15 giây để có lần vẽ có ý nghĩa đầu tiên hoặc để ứng dụng có thể tương tác. Lighthouse đã nêu bật rất nhiều vấn đề với trang web của chúng tôi và điểm hiệu suất tổng thể là 23 phản ánh chính xác điều đó.

Trang có trọng số khoảng 3,4 MB – chúng tôi cần phải cắt giảm một số nội dung không cần thiết.

Việc này bắt đầu thử thách hiệu suất đầu tiên của chúng tôi: tìm những nội dung chúng tôi có thể dễ dàng xoá mà không ảnh hưởng đến trải nghiệm tổng thể.

Cơ hội tối ưu hoá hiệu suất

Xoá tài nguyên không cần thiết

Có một số nội dung rõ ràng có thể được xoá một cách an toàn: khoảng trắng và nhận xét.

Lợi ích của việc rút gọn
Hình 4. Rút gọn và nén JavaScript và CSS

Lighthouse nêu bật cơ hội này trong phần Kiểm tra CSS và JavaScript chưa được rút gọn. Chúng tôi đang sử dụng webpack cho quy trình xây dựng, vì vậy, để rút gọn, chúng tôi chỉ cần sử dụng trình bổ trợ Uglify JS.

Rút gọn là một tác vụ phổ biến, vì vậy, bạn có thể tìm thấy giải pháp sẵn sàng cho mọi quy trình xây dựng mà bạn sử dụng.

Một cách kiểm tra hữu ích khác trong không gian đó là Cho phép nén văn bản. Không có lý do gì để gửi tệp không nén và hầu hết CDN hiện nay đều hỗ trợ tính năng này.

Chúng tôi đang sử dụng Firebase Hosting để lưu trữ mã của mình, và Firebase bật tính năng nén gzip theo mặc định. Vì vậy, nhờ việc lưu trữ mã trên một CDN hợp lý, chúng tôi đã nhận được tính năng này miễn phí.

Mặc dù gzip là một phương thức nén rất phổ biến, nhưng các cơ chế khác như ZopfliBrotli cũng đang thu hút sự chú ý. Brotli thích hỗ trợ trong hầu hết các trình duyệt và bạn có thể sử dụng tệp nhị phân để nén trước thành phần trước khi gửi đến máy chủ.

Sử dụng chính sách bộ nhớ đệm hiệu quả

Bước tiếp theo là đảm bảo rằng chúng ta không gửi tài nguyên hai lần nếu không cần thiết.

Quy trình kiểm tra Chính sách về bộ nhớ đệm không hiệu quả trong Lighthouse giúp chúng tôi nhận thấy rằng chúng tôi có thể tối ưu hoá các chiến lược lưu vào bộ nhớ đệm để đạt được điều đó. Bằng cách đặt tiêu đề hết hạn thời gian tối đa trong máy chủ của chúng tôi, chúng tôi đảm bảo rằng khi truy cập nhiều lần, người dùng có thể sử dụng lại tài nguyên mà họ đã tải xuống trước đó.

Tốt nhất là bạn nên nhắm đến việc lưu càng nhiều tài nguyên vào bộ nhớ đệm càng tốt theo cách bảo mật trong khoảng thời gian lâu nhất có thể, đồng thời cung cấp mã xác thực để xác thực lại hiệu quả các tài nguyên đã cập nhật.

Xoá mã không dùng đến

Cho đến nay, chúng tôi đã xoá những phần rõ ràng của nội dung tải xuống không cần thiết, nhưng còn những phần ít rõ ràng hơn thì sao? Ví dụ: mã không dùng đến.

Mức độ sử dụng mã trong Công cụ cho nhà phát triển
Hình 5. Kiểm tra mức độ sử dụng mã

Đôi khi, chúng ta đưa vào mã ứng dụng những mã không thực sự cần thiết. Điều này đặc biệt xảy ra nếu bạn làm việc trên ứng dụng trong một khoảng thời gian dài hơn, nhóm hoặc phần phụ thuộc của bạn thay đổi và đôi khi một thư viện không còn được dùng đến sẽ bị bỏ lại. Đó chính xác là những gì đã xảy ra với chúng tôi.

Ban đầu, chúng tôi sử dụng thư viện Material Components để nhanh chóng tạo bản minh hoạ ứng dụng. Theo thời gian, chúng tôi chuyển sang giao diện tuỳ chỉnh hơn và hoàn toàn quên thư viện đó. Rất may là bước kiểm tra mức độ sử dụng mã đã giúp chúng tôi tìm lại tính năng này trong gói của mình.

Bạn có thể kiểm tra số liệu thống kê về mức độ sử dụng mã trong Công cụ cho nhà phát triển, cả trong thời gian chạy cũng như thời gian tải của ứng dụng. Bạn có thể thấy hai dải màu đỏ lớn trong ảnh chụp màn hình ở dưới cùng – chúng tôi có hơn 95% CSS không được sử dụng và một loạt JavaScript lớn.

Lighthouse cũng phát hiện vấn đề này trong quy trình kiểm tra quy tắc CSS không dùng đến. Kết quả cho thấy có thể tiết kiệm được hơn 400 kb. Vì vậy, chúng ta quay lại mã và xoá cả phần JavaScript và CSS của thư viện đó.

Nếu chúng ta xoá trình chuyển đổi MVC, thì các kiểu của chúng ta sẽ giảm xuống còn 10 KB
Hình 6. Nếu chúng ta loại bỏ bộ chuyển đổi MVC, thì các kiểu của chúng ta sẽ giảm xuống còn 10 KB!

Điều này giúp giảm gói CSS của chúng ta xuống 20 lần, khá tốt cho một thay đổi nhỏ, dài 2 dòng.

Tất nhiên, điều này đã làm điểm hiệu suất của chúng tôi tăng lên, đồng thời Thời gian phản hồi cũng tốt hơn nhiều.

Tuy nhiên, với những thay đổi như vậy, bạn không chỉ cần kiểm tra các chỉ số và điểm số của mình. Việc xoá mã thực tế không bao giờ an toàn, vì vậy, bạn phải luôn chú ý đến các trường hợp hồi quy tiềm ẩn.

Mã của chúng tôi không được sử dụng trong 95% trường hợp – vẫn còn 5% trường hợp. Có vẻ như một trong các thành phần của chúng ta vẫn đang sử dụng các kiểu từ thư viện đó – các mũi tên nhỏ trong thanh trượt vẽ nguệch ngoạc. Tuy nhiên, vì kích thước quá nhỏ nên chúng tôi chỉ cần kết hợp các kiểu đó trở lại vào các nút theo cách thủ công.

Các nút bị hỏng do thiếu thư viện
Hình 7. Một thành phần vẫn đang sử dụng thư viện đã xoá

Vì vậy, nếu bạn xoá mã, hãy đảm bảo rằng bạn có quy trình kiểm thử thích hợp để giúp bạn bảo vệ khỏi các trường hợp hồi quy hình ảnh tiềm ẩn.

Tránh các tài nguyên lớn trên mạng

Chúng tôi biết rằng các tài nguyên lớn có thể làm chậm tốc độ tải trang web. Các tệp này có thể khiến người dùng tốn tiền và ảnh hưởng lớn đến gói dữ liệu của họ. Vì vậy, bạn cần lưu ý đến điều này.

Lighthouse có thể phát hiện rằng chúng ta gặp vấn đề với một số tải trọng mạng bằng cách sử dụng quy trình kiểm tra Tải trọng mạng khổng lồ.

Phát hiện tải trọng mạng khổng lồ
Hình 8. Phát hiện tải trọng mạng khổng lồ

Ở đây, chúng ta thấy rằng có hơn 3mb mã đang được gửi xuống – một lượng khá lớn, đặc biệt là trên thiết bị di động.

Ở đầu danh sách này, Lighthouse nhấn mạnh rằng chúng ta có một gói nhà cung cấp JavaScript là 2 MB mã không nén. Đây cũng là vấn đề mà webpack nêu bật.

Như đã nói: yêu cầu nhanh nhất là yêu cầu không được đưa ra.

Tốt nhất là bạn nên đo lường giá trị của từng thành phần mà bạn đang phân phát cho người dùng, đo lường hiệu suất của các thành phần đó và gọi điện xem liệu có đáng thực sự loại bỏ thành phần đó trong trải nghiệm ban đầu hay không. Bởi vì đôi khi, những thành phần này có thể bị trì hoãn, tải từng phần hay xử lý trong thời gian không hoạt động.

Trong trường hợp của chúng ta, vì phải xử lý nhiều gói JavaScript, nên chúng ta rất may mắn vì cộng đồng JavaScript có một bộ công cụ kiểm tra gói JavaScript phong phú.

Kiểm tra gói JavaScript
Hình 9. Kiểm tra gói JavaScript

Chúng ta bắt đầu với trình phân tích gói webpack, trình phân tích này cho chúng ta biết rằng chúng ta đang đưa vào một phần phụ thuộc có tên là unicode, là 1,6 MB JavaScript đã được phân tích cú pháp, khá lớn.

Sau đó, chúng tôi chuyển sang trình chỉnh sửa và sử dụng Trình bổ trợ chi phí nhập cho mã hình ảnh để có thể trực quan hoá chi phí của mọi mô-đun mà chúng tôi đang nhập. Điều này cho phép chúng ta khám phá thành phần nào có chứa mã tham chiếu đến mô-đun này.

Sau đó, chúng tôi chuyển sang một công cụ khác, BundlePhobia. Đây là một công cụ cho phép bạn nhập tên của bất kỳ gói GMS nào và thực sự xem kích thước rút gọn và nén của gói được ước tính là bao nhiêu. Chúng tôi đã tìm thấy một giải pháp thay thế phù hợp cho mô-đun slug mà chúng tôi đang sử dụng chỉ có kích thước 2,2 kb, vì vậy, chúng tôi đã chuyển sang sử dụng giải pháp đó.

Điều này đã có tác động lớn đến hiệu suất của chúng tôi. Giữa thay đổi này và khám phá các cơ hội khác để giảm kích thước gói JavaScript, chúng tôi đã lưu 2,1 MB mã.

Nhìn chung, chúng tôi nhận thấy các gói này đã cải thiện được 65% sau khi bạn tính đến kích thước của các gói được nén và giảm kích thước. Và chúng tôi thấy rằng việc này thực sự đáng để thực hiện theo một quy trình.

Vì vậy, nói chung, hãy cố gắng loại bỏ các tệp tải xuống không cần thiết trong trang web và ứng dụng của bạn. Việc lập khoảng không quảng cáo cho các thành phần và đo lường tác động của các thành phần đó đến hiệu suất có thể tạo ra sự khác biệt rất lớn. Vì vậy, hãy đảm bảo rằng bạn thường xuyên kiểm tra các thành phần của mình.

Giảm thời gian khởi động JavaScript bằng tính năng phân tách mã

Mặc dù tải trọng mạng lớn có thể ảnh hưởng lớn đến ứng dụng của chúng ta, nhưng còn một yếu tố khác có thể ảnh hưởng rất lớn, đó là JavaScript.

JavaScript là tài sản đắt nhất. Trên thiết bị di động, nếu bạn đang gửi các gói JavaScript lớn, thì điều này có thể làm chậm thời gian người dùng có thể tương tác với các thành phần giao diện người dùng. Điều đó có nghĩa là họ có thể nhấn vào giao diện người dùng mà không có gì có ý nghĩa thực sự xảy ra. Vì vậy, điều quan trọng là chúng ta phải hiểu lý do khiến JavaScript tốn kém như vậy.

Đây là cách trình duyệt xử lý JavaScript.

Xử lý JavaScript
Hình 10. Xử lý JavaScript

Trước tiên, chúng ta phải tải tập lệnh đó xuống, sau đó chúng ta có một công cụ JavaScript cần phân tích cú pháp mã đó, cần biên dịch và thực thi mã đó.

Giờ đây, các giai đoạn này không mất nhiều thời gian trên một thiết bị cao cấp như máy tính để bàn hoặc máy tính xách tay, thậm chí có thể là điện thoại cao cấp. Nhưng trên một điện thoại di động trung bình, quá trình này có thể mất từ 5 đến 10 lần lâu hơn. Đây là nguyên nhân làm chậm hoạt động tương tác, vì vậy, điều quan trọng là chúng ta phải cố gắng cắt giảm điều này.

Để giúp bạn phát hiện những vấn đề này với ứng dụng của mình, chúng tôi đã ra mắt một tính năng mới là kiểm tra thời gian khởi động JavaScript cho Lighthouse.

Thời gian khởi động JavaScript
Hình 11. Kiểm tra thời gian khởi động JavaScript

Và trong trường hợp của ứng dụng Oodle, nó cho chúng tôi biết rằng chúng tôi có 1,8 giây thời gian để khởi động JavaScript. Điều đã xảy ra là chúng tôi đang nhập tĩnh tất cả các tuyến và thành phần vào một gói JavaScript nguyên khối.

Một kỹ thuật để giải quyết vấn đề này là sử dụng phân tách mã.

Việc phân tách mã giống như pizza

Phân tách mã là khái niệm thay vì cung cấp cho người dùng toàn bộ JavaScript, nếu bạn chỉ cung cấp cho họ một lát pizza mỗi khi họ cần thì sao?

Bạn có thể áp dụng tính năng phân tách mã ở cấp tuyến hoặc cấp thành phần. Thư viện này hoạt động hiệu quả với React và React Loadable, Vue.js, Angular, Polymer, Preact và nhiều thư viện khác.

Chúng tôi đã tích hợp tính năng phân tách mã vào ứng dụng, chuyển từ nhập tĩnh sang nhập động, cho phép chúng tôi tải mã tải lười không đồng bộ khi cần.

Phân tách mã bằng tính năng nhập động
Hình 13. Phân tách mã bằng các lệnh nhập động

Điều này vừa giúp giảm kích thước gói, vừa giảm thời gian khởi động JavaScript. Thời gian này giảm xuống còn 0,78 giây, giúp ứng dụng nhanh hơn 56%.

Nhìn chung, nếu bạn đang xây dựng một trải nghiệm nặng về JavaScript, hãy nhớ chỉ gửi mã đến người dùng mà họ cần.

Tận dụng các khái niệm như phân tách mã, khám phá các ý tưởng như loại bỏ mã không dùng đến và xem kho lưu trữ webpack-libs-optimizations để biết một số ý tưởng về cách giảm kích thước thư viện nếu bạn đang sử dụng webpack.

Tối ưu hóa hình ảnh

Chuyện cười về hiệu suất tải hình ảnh

Trong ứng dụng Oodle, chúng ta đang sử dụng rất nhiều hình ảnh. Rất tiếc, Lighthouse không hào hứng về điều này như chúng tôi. Thực tế là chúng tôi đã không vượt qua cả 3 quy trình kiểm tra liên quan đến hình ảnh.

Chúng ta đã quên tối ưu hoá hình ảnh, không định cỡ hình ảnh chính xác và cũng có thể nhận được một số lợi ích khi sử dụng các định dạng hình ảnh khác.

Kiểm tra hình ảnh
Hình 14. Bài kiểm tra hình ảnh bằng Lighthouse

Chúng tôi bắt đầu bằng cách tối ưu hoá hình ảnh.

Đối với vòng tối ưu hoá một lần, bạn có thể sử dụng các công cụ trực quan như ImageOptim hoặc XNConvert.

Một phương pháp tự động hơn là thêm bước tối ưu hoá hình ảnh vào quy trình xây dựng, với các thư viện như imagemin.

Bằng cách này, bạn đảm bảo rằng những hình ảnh được thêm trong tương lai sẽ được tự động tối ưu hoá. Một số CDN, chẳng hạn như Akamai hoặc các giải pháp của bên thứ ba như Cloudinary, Fastly hoặc Uploadcare cung cấp cho bạn các giải pháp tối ưu hoá hình ảnh toàn diện. Vì vậy, bạn cũng có thể lưu trữ hình ảnh trên các dịch vụ đó.

Nếu bạn không muốn làm như vậy do chi phí hoặc vấn đề về độ trễ, các dự án như Thumbor hoặc Imageflow sẽ cung cấp các giải pháp thay thế tự lưu trữ.

Trước và sau khi tối ưu hoá
Hình 15. Trước và sau khi tối ưu hoá

PNG nền của chúng tôi đã bị gắn cờ trong webpack là lớn và hoàn toàn đúng. Sau khi định kích thước hình ảnh chính xác cho khung nhìn và chạy thông qua ImageOptim, chúng tôi đã giảm xuống còn 100 kb, là chấp nhận được.

Việc lặp lại vấn đề này cho nhiều hình ảnh trên trang web cho phép chúng tôi giảm đáng kể trọng số tổng thể của trang.

Sử dụng định dạng phù hợp cho nội dung động

Ảnh GIF có thể rất tốn kém. Điều đáng ngạc nhiên là ngay từ đầu, định dạng GIF chưa bao giờ được dùng làm nền tảng ảnh động. Do đó, việc chuyển sang một định dạng video phù hợp hơn sẽ giúp bạn tiết kiệm đáng kể về kích thước tệp.

Trong ứng dụng Oodle, chúng tôi đang sử dụng ảnh GIF làm trình tự giới thiệu trên trang chủ. Theo Lighthouse, chúng ta có thể tiết kiệm hơn 7 MB bằng cách chuyển sang định dạng video hiệu quả hơn. Đoạn video của chúng tôi có trọng số khoảng 7,3 MB, quá nhiều so với bất kỳ trang web hợp lý nào.Vì vậy, chúng tôi đã chuyển đoạn video đó thành một phần tử video có 2 tệp nguồn – một mp4 và WebM để hỗ trợ trình duyệt rộng hơn.

Thay thế ảnh GIF động bằng video
Hình 16. Thay thế ảnh GIF động bằng video

Chúng ta sử dụng công cụ FFmpeg để chuyển đổi ảnh động GIF thành tệp mp4. Định dạng WebM giúp bạn tiết kiệm nhiều hơn nữa – API ImageOptim có thể thực hiện việc chuyển đổi như vậy cho bạn.

ffmpeg -i animation.gif -b:v 0 -crf 40 -vf scale=600:-1 video.mp4

Nhờ việc chuyển đổi này, chúng tôi đã tiết kiệm được hơn 80% trọng lượng tổng thể. Điều này đã giúp chúng tôi giảm xuống còn khoảng 1mb.

Tuy nhiên, 1mb là một tài nguyên lớn để đẩy xuống dây, đặc biệt là đối với người dùng có băng thông bị hạn chế. May mắn là chúng ta có thể sử dụng Effective Type API để nhận ra rằng họ đang có băng thông chậm và cung cấp cho họ một tệp JPEG nhỏ hơn nhiều.

Giao diện này sử dụng giá trị thời gian khứ hồi và giá trị giảm xuống hiệu quả để ước tính loại mạng mà người dùng đang sử dụng. Phương thức này chỉ trả về một chuỗi, 2G chậm, 2G, 3G hoặc 4G. Vì vậy, tuỳ thuộc vào giá trị này, nếu người dùng đang sử dụng mạng dưới 4G, chúng ta có thể thay thế phần tử video bằng hình ảnh.

if (navigator.connection.effectiveType) { ... }

Điều này làm giảm một chút trải nghiệm, nhưng ít nhất là trang web vẫn có thể sử dụng được khi có kết nối chậm.

Tải từng phần hình ảnh ngoài màn hình

Băng chuyền, thanh trượt hoặc các trang thực sự dài thường tải hình ảnh, mặc dù người dùng không thể thấy ngay hình ảnh trên trang.

Lighthouse sẽ gắn cờ hành vi này trong quy trình kiểm tra hình ảnh ngoài màn hình và bạn cũng có thể tự xem hành vi này trong bảng điều khiển mạng của DevTools. Nếu bạn thấy rất nhiều hình ảnh được gửi đến trong khi chỉ một vài hình ảnh hiển thị trên trang, thì tức là bạn nên cân nhắc việc tải từng phần hình ảnh.

Tính năng tải lười chưa được hỗ trợ gốc trong trình duyệt, vì vậy, chúng ta phải sử dụng JavaScript để thêm tính năng này. Chúng tôi đã sử dụng thư viện Lazysizes để thêm hành vi tải lười vào các ảnh bìa Oodle.

<!-- Import library -->
import lazysizes from 'lazysizes'  <!-- or -->
<script src="lazysizes.min.js"></script>

<!-- Use it -->

<img data-src="image.jpg" class="lazyload"/>
<img class="lazyload"
    data-sizes="auto"
    data-src="image2.jpg"
    data-srcset="image1.jpg 300w,
    image2.jpg 600w,
    image3.jpg 900w"/>

Lazysizes rất thông minh vì không chỉ theo dõi các thay đổi về chế độ hiển thị của phần tử, mà còn chủ động tìm nạp trước các phần tử ở gần khung hiển thị để có trải nghiệm người dùng tối ưu. Thư viện này cũng cung cấp tuỳ chọn tích hợp IntersectionObserver, giúp bạn tra cứu chế độ hiển thị rất hiệu quả.

Sau thay đổi này, hình ảnh của chúng tôi sẽ được tìm nạp theo yêu cầu. Nếu bạn muốn tìm hiểu sâu hơn về chủ đề đó, hãy xem images.guide – một tài nguyên rất hữu ích và toàn diện.

Giúp trình duyệt phân phối sớm các tài nguyên quan trọng

Không phải mỗi byte được gửi qua mạng đến trình duyệt đều có mức độ quan trọng như nhau và trình duyệt biết điều này. Nhiều trình duyệt có phương pháp phỏng đoán để quyết định nội dung cần tìm nạp trước tiên. Vì vậy, đôi khi, các trình duyệt sẽ tìm nạp CSS trước hình ảnh hoặc tập lệnh.

Một điều có thể hữu ích là chúng tôi, với tư cách là tác giả của trang, cung cấp cho trình duyệt những nội dung thực sự quan trọng đối với chúng tôi. Rất may, trong vài năm qua, các nhà cung cấp trình duyệt đã thêm một số tính năng để giúp chúng tôi giải quyết vấn đề này, chẳng hạn như gợi ý tài nguyên như link rel=preconnect, preload hoặc prefetch.

Những chức năng này được đưa vào nền tảng web giúp trình duyệt tìm nạp nội dung phù hợp vào đúng thời điểm và có thể hiệu quả hơn một chút so với một số phương pháp tải tuỳ chỉnh, dựa trên logic được thực hiện bằng tập lệnh.

Hãy xem Lighthouse thực sự hướng dẫn chúng ta sử dụng một số tính năng trong số này hiệu quả như thế nào.

Điều đầu tiên mà Lighthouse yêu cầu chúng ta làm là tránh nhiều lượt đi và về tốn kém đến bất kỳ nguồn gốc nào.

Tránh nhiều chuyến đi và về tốn kém đến bất kỳ nguồn gốc nào
Hình 17. Tránh nhiều lượt truy cập khứ hồi tốn kém đến bất kỳ nguồn gốc nào

Trong trường hợp của ứng dụng Oodle, chúng tôi thực sự sử dụng nhiều Google Fonts. Bất cứ khi nào bạn thả một trang kiểu phông chữ Google vào trang, trang kiểu đó sẽ kết nối tối đa hai miền con. Và Lighthouse cho chúng tôi biết rằng nếu chúng tôi có thể hâm nóng kết nối đó, chúng tôi có thể tiết kiệm được tới 300 mili giây trong thời gian kết nối ban đầu.

Bằng cách tận dụng tính năng liên kết rel preconnect, chúng ta có thể che giấu hiệu quả độ trễ kết nối đó.

Đặc biệt là với những định dạng như Google Fonts, trong đó CSS của mặt phông chữ được lưu trữ trên googleapis.com và các tài nguyên phông chữ của chúng tôi được lưu trữ trên Gstatic, thì điều này có thể gây ra tác động thực sự lớn. Vì vậy, chúng tôi áp dụng phương pháp tối ưu hoá này và giảm bớt vài trăm mili giây.

Điều tiếp theo mà Lighthouse đề xuất là chúng ta nên tải trước các yêu cầu chính.

Tải trước các yêu cầu chính
Hình 18. Tải trước các yêu cầu chính

<link rel=preload> thực sự mạnh mẽ, nó thông báo cho trình duyệt rằng cần có một tài nguyên trong quá trình điều hướng hiện tại và cố gắng để trình duyệt tìm nạp tài nguyên đó sớm nhất có thể.

Bây giờ, Lighthouse cho chúng ta biết rằng chúng ta nên tải trước các tài nguyên phông chữ chính trên web vì chúng ta đang tải bằng 2 phông chữ trên web.

Tải trước trong phông chữ web sẽ có dạng như sau – chỉ định rel=preload, bạn truyền vào as với loại phông chữ, sau đó chỉ định loại phông chữ bạn đang cố gắng tải, chẳng hạn như woff2.

Điều này có thể ảnh hưởng rất lớn đến trang của bạn.

Tác động của việc tải trước tài nguyên
Hình 19. Tác động của việc tải trước tài nguyên

Thông thường, nếu không sử dụng tính năng tải trước đường liên kết rel, nếu phông chữ web lại quan trọng đối với trang của bạn, thì điều trình duyệt phải làm trước tiên là phải tìm nạp HTML, phải phân tích cú pháp CSS và sau đó, cuối cùng trình duyệt sẽ tìm nạp phông chữ web.

Khi sử dụng tính năng tải trước liên kết rel, ngay khi trình duyệt phân tích cú pháp HTML, trình duyệt có thể bắt đầu tìm nạp các phông chữ web đó sớm hơn nhiều. Trong trường hợp của ứng dụng, việc này có thể giúp giảm một giây thời gian hiển thị văn bản bằng phông chữ web.

Giờ đây, việc tải trước phông chữ bằng Google Fonts không đơn giản như vậy, vì có một điểm cần lưu ý.

URL Phông chữ Google mà chúng ta chỉ định trên các mặt phông chữ trong các tệp định kiểu lại là nội dung mà nhóm phông chữ cập nhật khá thường xuyên. Các URL này có thể hết hạn hoặc được cập nhật theo tần suất thường xuyên. Vì vậy, nếu bạn muốn kiểm soát hoàn toàn trải nghiệm tải phông chữ, bạn nên tự lưu trữ phông chữ web. Điều này có thể rất hữu ích vì cho phép bạn truy cập vào các tính năng như tải trước liên kết rel.

Trong trường hợp của chúng tôi, chúng tôi nhận thấy công cụ Trình trợ giúp phông chữ web của Google thực sự hữu ích trong việc giúp chúng tôi tải một số phông chữ web đó xuống và thiết lập cục bộ. Vì vậy, hãy kiểm tra công cụ đó.

Cho dù bạn đang sử dụng phông chữ trên web trong tài nguyên quan trọng của mình hay là JavaScript, hãy cố gắng giúp trình duyệt phân phối tài nguyên quan trọng càng sớm càng tốt.

Thử nghiệm: Gợi ý về mức độ ưu tiên

Hôm nay, chúng tôi có một thông tin đặc biệt muốn chia sẻ với bạn. Ngoài các tính năng như gợi ý tài nguyên và tải trước, chúng tôi đang phát triển một tính năng trình duyệt thử nghiệm hoàn toàn mới có tên là gợi ý mức độ ưu tiên.

Đặt mức độ ưu tiên cho nội dung hiển thị ban đầu
Hình 20. Gợi ý ưu tiên

Đây là tính năng mới cho phép bạn gợi ý cho trình duyệt về tầm quan trọng của một tài nguyên. Tính năng này cho biết một thuộc tính mới — mức độ quan trọng — với các giá trị thấp, cao hoặc tự động.

Điều này cho phép chúng tôi truyền tải việc giảm mức độ ưu tiên của các tài nguyên ít quan trọng hơn, chẳng hạn như kiểu không quan trọng, hình ảnh hoặc tìm nạp lệnh gọi API để giảm tình trạng tranh chấp. Chúng ta cũng có thể tăng mức độ ưu tiên của những nội dung quan trọng hơn, chẳng hạn như hình ảnh chính.

Trong trường hợp ứng dụng Oodle, điều này thực sự đã dẫn đến một nơi thực tế mà chúng ta có thể tối ưu hoá.

Đặt mức độ ưu tiên cho nội dung hiển thị ban đầu
Hình 21. Đặt mức độ ưu tiên cho nội dung hiển thị ban đầu

Trước khi chúng tôi thêm tính năng tải từng phần vào hình ảnh của mình, công việc của trình duyệt là chúng tôi có băng chuyền hình ảnh này với tất cả các hình tượng trưng của chúng tôi và trình duyệt đang tìm nạp tất cả hình ảnh ngay từ đầu băng chuyền với mức độ ưu tiên cao ngay từ đầu. Tuy nhiên, hình ảnh ở giữa băng chuyền lại là quan trọng nhất đối với người dùng. Vì vậy, chúng tôi đã đặt mức độ quan trọng của các hình nền đó ở mức rất thấp, còn hình nền trước ở mức rất cao. Điều này đã tạo ra tác động 2 giây trên mạng 3G chậm và tốc độ chúng tôi có thể tìm nạp và hiển thị các hình ảnh đó. Vì vậy, đây là một trải nghiệm tích cực.

Chúng tôi hy vọng sẽ cung cấp tính năng này cho Canary sau vài tuần nữa, vì vậy hãy chú ý theo dõi.

Có chiến lược tải phông chữ trên web

Kiểu chữ là yếu tố cơ bản để thiết kế hiệu quả. Nếu đang sử dụng phông chữ web, bạn không nên chặn quá trình kết xuất văn bản và chắc chắn không nên hiển thị văn bản không hiển thị.

Chúng tôi hiện đã nêu bật vấn đề này trong Lighthouse bằng quy trình kiểm tra tránh văn bản không hiển thị trong khi phông chữ web đang tải.

Tránh hiển thị văn bản bị ẩn trong khi phông chữ trên web đang tải
Hình 22. Tránh văn bản không hiển thị trong khi Phông chữ web đang tải

Nếu tải phông chữ trên web bằng một khối mặt phông chữ, bạn sẽ cho phép trình duyệt quyết định việc cần làm nếu mất nhiều thời gian để tìm nạp phông chữ đó trên web. Một số trình duyệt sẽ đợi từ 1 đến 3 giây trước khi quay lại phông chữ hệ thống và cuối cùng sẽ hoán đổi phông chữ đó với phông chữ đã tải xuống.

Chúng tôi đang cố gắng tránh văn bản vô hình này, vì vậy, trong trường hợp này, chúng tôi sẽ không thể xem các bức vẽ nguệch ngoạc cổ điển của tuần này nếu phông chữ web mất quá nhiều thời gian. Rất may là với một tính năng mới có tên là font-display, bạn sẽ có nhiều quyền kiểm soát hơn đối với quy trình này.

    @font-face {
      font-family: 'Montserrat';
      font-style: normal;
      font-display: swap;
      font-weight: 400;
      src: local('Montserrat Regular'), local('Montserrat-Regular'),
          /* Chrome 26+, Opera 23+, Firefox 39+ */
          url('montserrat-v12-latin-regular.woff2') format('woff2'),
            /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */
          url('montserrat-v12-latin-regular.woff') format('woff');
    }

Hiển thị phông chữ giúp bạn quyết định cách phông chữ web hiển thị hoặc dự phòng dựa trên thời gian cần thiết để hoán đổi phông chữ.

Trong trường hợp này, chúng ta sẽ sử dụng tính năng hoán đổi chế độ hiển thị phông chữ. Thao tác hoán đổi sẽ cung cấp cho mặt phông chữ một khoảng thời gian chặn bằng 0 giây và một khoảng thời gian hoán đổi vô hạn. Điều này có nghĩa là trình duyệt sẽ vẽ văn bản của bạn ngay lập tức bằng phông chữ dự phòng nếu phông chữ mất một chút thời gian để tải. Và sẽ hoán đổi phông chữ này khi có phông chữ.

Trong trường hợp của ứng dụng, lý do khiến việc này rất tuyệt vời là vì nó cho phép chúng tôi hiển thị một số văn bản có ý nghĩa ngay từ đầu và chuyển sang phông chữ web khi phông chữ đó đã sẵn sàng.

Kết quả hiển thị phông chữ
Hình 23. Kết quả hiển thị phông chữ

Nhìn chung, nếu bạn đang sử dụng phông chữ web (như phần lớn trang web), hãy có một chiến lược tải phông chữ web hiệu quả.

Có rất nhiều tính năng nền tảng web mà bạn có thể sử dụng để tối ưu hoá trải nghiệm tải phông chữ, nhưng cũng hãy xem kho lưu trữ Web Font Recipes của Zach Leatherman, vì kho lưu trữ này thực sự rất tuyệt vời.

Giảm số lượng tập lệnh chặn kết xuất

Có những phần khác trong ứng dụng mà chúng tôi có thể đẩy sớm hơn trong chuỗi tải xuống để cung cấp ít nhất một số trải nghiệm cơ bản sớm hơn cho người dùng.

Trên dải dòng thời gian Lighthouse, bạn có thể thấy rằng trong vài giây đầu tiên khi tất cả tài nguyên đang tải, người dùng thực sự không thể nhìn thấy bất kỳ nội dung nào.

Giảm cơ hội tệp kiểu chặn hiển thị
Hình 24. Giảm cơ hội tệp kiểu chặn hiển thị

Việc tải xuống và xử lý các tệp định kiểu bên ngoài đang chặn quá trình kết xuất của chúng ta.

Chúng ta có thể cố gắng tối ưu hoá đường dẫn kết xuất quan trọng bằng cách phân phối một số kiểu sớm hơn một chút.

Nếu chúng ta trích xuất các kiểu chịu trách nhiệm cho lần hiển thị ban đầu này và đưa các kiểu đó vào cùng dòng trong HTML của chúng tôi, thì trình duyệt có thể hiển thị các kiểu đó ngay mà không cần đợi các biểu định kiểu bên ngoài đến.

Trong trường hợp này, chúng tôi đã sử dụng một mô-đun NPM có tên là Critical (Quan trọng) để cùng dòng nội dung quan trọng trong index.html trong bước tạo bản dựng.

Mặc dù mô-đun này đã thực hiện hầu hết các công việc nặng cho chúng ta, nhưng vẫn hơi khó để làm cho mô-đun này hoạt động trơn tru trên nhiều tuyến.

Nếu bạn không cẩn thận hoặc cấu trúc trang web của bạn thực sự phức tạp, thì bạn có thể sẽ rất khó để giới thiệu loại mẫu này nếu bạn không lên kế hoạch cho cấu trúc vỏ ứng dụng ngay từ đầu.

Đây là lý do tại sao việc sớm cân nhắc về hiệu suất lại quan trọng đến vậy. Nếu không thiết kế cho hiệu suất ngay từ đầu, nhiều khả năng bạn sẽ gặp phải vấn đề sau này.

Cuối cùng, rủi ro của chúng tôi đã được đền đáp, chúng tôi đã có thể làm cho nó hoạt động và ứng dụng bắt đầu phân phối nội dung sớm hơn nhiều, cải thiện đáng kể thời gian vẽ đầu tiên có ý nghĩa.

Kết quả

Đó là một danh sách dài các phương pháp tối ưu hoá hiệu suất mà chúng tôi đã áp dụng cho trang web của mình. Hãy cùng xem kết quả. Đây là cách ứng dụng của chúng tôi tải trên một thiết bị di động trung bình trên mạng 3G, trước và sau khi tối ưu hoá.

Điểm hiệu suất Lighthouse tăng từ 23 lên 91. Đó là một tiến trình khá tốt về tốc độ. Tất cả thay đổi đều là nhờ chúng tôi liên tục kiểm tra và làm theo báo cáo Lighthouse. Nếu bạn muốn xem cách chúng tôi triển khai tất cả các điểm cải tiến về mặt kỹ thuật, hãy tham khảo kho lưu trữ của chúng tôi, đặc biệt là các yêu cầu thay đổi đã được đưa vào kho lưu trữ đó.

Hiệu suất dự đoán – trải nghiệm người dùng dựa trên dữ liệu

Chúng tôi tin rằng công nghệ học máy là một cơ hội thú vị cho tương lai trong nhiều lĩnh vực. Một ý tưởng mà chúng tôi hy vọng sẽ tạo ra nhiều thử nghiệm hơn trong tương lai, đó là dữ liệu thực thực sự có thể định hướng trải nghiệm người dùng mà chúng tôi đang tạo ra.

Ngày nay, chúng ta đưa ra rất nhiều quyết định tuỳ ý về những gì người dùng có thể muốn hoặc cần, do đó, những nội dung nào đáng được tìm nạp trước, tải trước hoặc lưu vào bộ nhớ đệm trước. Nếu đoán đúng, chúng ta có thể ưu tiên một lượng nhỏ tài nguyên, nhưng rất khó để mở rộng quy mô cho toàn bộ trang web.

Chúng tôi hiện có dữ liệu để hỗ trợ việc tối ưu hoá hiệu quả hơn. Khi sử dụng API báo cáo của Google Analytics, chúng ta có thể xem trang hàng đầu tiếp theo và tỷ lệ phần trăm thoát cho bất kỳ URL nào trên trang web của mình, từ đó đưa ra kết luận về tài nguyên nào chúng ta nên ưu tiên.

Nếu kết hợp điều này với một mô hình xác suất tốt, chúng ta có thể tránh lãng phí dữ liệu của người dùng bằng cách tích cực tải trước nội dung. Chúng ta có thể tận dụng dữ liệu Google Analytics đó, đồng thời sử dụng công nghệ học máy và các mô hình như chuỗi Markov hoặc mạng nơron để triển khai các mô hình như vậy.

Gói dựa trên dữ liệu cho ứng dụng web
Hình 25. Gói dựa trên dữ liệu cho ứng dụng web

Để hỗ trợ thử nghiệm này, chúng tôi rất vui được thông báo về một sáng kiến mới có tên là Guess.js.

Guess.js
Hình 26. Guess.js

Guess.js là một dự án tập trung vào trải nghiệm người dùng dựa trên dữ liệu cho web. Chúng tôi hy vọng rằng điều này sẽ truyền cảm hứng cho bạn khám phá việc sử dụng dữ liệu để cải thiện hiệu suất web và làm được nhiều việc hơn thế. Tất cả đều là nguồn mở và hiện có trên GitHub. Công cụ này được xây dựng bằng cách cộng tác với cộng đồng nguồn mở của Minko Gechev, Kyle Matthews từ Gatsby, Katie Hempenius và một số người khác.

Hãy dùng thử Guess.js và cho chúng tôi biết ý kiến của bạn.

Tóm tắt

Điểm số và chỉ số rất hữu ích trong việc cải thiện tốc độ của Web, nhưng chúng chỉ là phương tiện, chứ không phải là mục tiêu.

Chúng ta đều đã từng trải nghiệm tình trạng tải trang chậm khi di chuyển, nhưng giờ đây, chúng ta có cơ hội mang đến cho người dùng trải nghiệm thú vị hơn và tải nhanh hơn.

Cải thiện hiệu suất là cả một hành trình. Nhiều thay đổi nhỏ có thể mang lại lợi ích lớn. Bằng cách sử dụng các công cụ tối ưu hoá phù hợp và theo dõi báo cáo của Lighthouse, bạn có thể mang đến trải nghiệm tốt hơn và toàn diện hơn cho người dùng.

Xin cảm ơn đặc biệt đến: Ward Peeters, Minko Gechev, Kyle Mathews, Katie Hempenius, Dom Farolino, Yoav Weiss, Susie Lu, Yusuke Utsunomiya, Tom Ankers, Lighthouse và Google Doodles.