Hiệu suất web dễ dàng – Phiên bản Google I/O 2018

Tại Google IO 2018, chúng tôi đã trình bày một loạt công cụ, thư viện và kỹ thuật tối ưu hoá giúp cải thiện hiệu suất web dễ dàng hơn. Tại đây, chúng tôi giải thích các kỹ thuật này bằng ứng dụng Oodles Theater. Chúng tôi cũng nói về các thử nghiệm của mình với tính năng tải dự đoán và sáng kiến Guess.js mới.

Ewa Gasperowicz

Trong năm qua, chúng tôi đã nỗ lực tìm cách tăng tốc độ và hiệu suất của web. Đ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 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 tôi sẽ nói về các thử nghiệm của mình với tính năng tải dự đoán và sáng kiến Guess.js mới.

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

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

Kích thước ngày càng tăng 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 hiển thị hoặc mã của bên thứ ba thừa, góp phần tạo nên bài toán hiệu suất phức tạp.

Hầu hết người dùng đều đánh giá tốc độ là yếu tố quan trọng nhất trong hệ thống 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ạc nhiên vì bạn không thể làm gì nhiều cho đến khi trang tải xong. Bạn không thể nhận được giá trị từ trang, bạn không thể chiêm ngưỡng tính thẩm mỹ của trang.

Kim tự tháp phân cấp trải nghiệm người dùng
Hình 1. Tốc độ quan trọng như thế nào đối với người dùng? (Speed Matters, Vol. 3)

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

Lighthouse – Nền tảng cho quy trình hiệu suất

Lighthouse là một phần của Chrome DevTools, giúp bạn kiểm tra trang web 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 bản 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.

Các quy trình kiểm tra mới của Lighthouse
Hình 2. Các quy trình kiểm tra mới của Lighthouse

Hãy cùng khám phá cách bạn có thể tận dụng các tính năng này qua một ví dụ thực tế: Ứng dụng Oodles Theater. Đây là một ứng dụng web minh hoạ nhỏ, nơi bạn có thể thử một số hình tượng trưng tương tác của Google 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 xây dựng ứng dụng, chúng tôi muốn đảm bảo rằng ứng dụng có hiệu suất cao nhất có thể. Điểm xuất phát cho việc tối ưu hoá là 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 để hiển thị nội dung đầ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 này có dung lượng khoảng 3,4 MB – chúng tôi rất cần cắt giảm một số phần dư thừa.

Đây là thử thách đầu tiên về hiệu suất: tìm những thứ chúng ta có thể dễ dàng loại bỏ 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 từ 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 bản kiểm tra CSS và JavaScript chưa được rút gọn. Chúng tôi đã 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 một giải pháp có sẵn cho bất kỳ quy trình xây dựng nào mà bạn sử dụng.

Một quy trình kiểm tra hữu ích khác trong không gian đó là Bật tính năng 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 đều hỗ trợ tính năng này ngay từ đầu.

Chúng tôi đã sử dụng Lưu trữ Firebase để lưu trữ mã của mình, và Firebase bật tính năng gzipping theo mặc định, vì vậy, nhờ lợi ích của 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 đó miễn phí.

Mặc dù gzip là một cách nén rất phổ biến, nhưng các cơ chế khác như Zopfli và Brotli cũng đang dần được ưa chuộng. Brotli được 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 cá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 tôi không gửi tài nguyên hai lần nếu không cần thiết.

Quá trình kiểm tra Chính sách lưu vào 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á chiến lược lưu vào bộ nhớ đệm để đạt được chính xác điều đó. Bằng cách đặt tiêu đề hết hạn max-age trong máy chủ, chúng tôi đảm bảo rằng khi truy cập lại, người dùng có thể sử dụng lại các tài nguyên mà họ đã tải xuống trước đó.

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

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

Cho đến nay, chúng ta đã xoá các phần rõ ràng của tệp 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 ứng dụng những đoạn 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 thư viện mồ côi 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 Thành phần Material để nhanh chóng tạo nguyên mẫu cho ứng dụng. Sau đó, chúng tôi chuyển sang giao diện tuỳ chỉnh hơn và quên hoàn toàn thư viện đó. May mắn là quy trình kiểm tra mức độ sử dụng mã đã giúp chúng tôi tìm lại được mã đó 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ả về 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 lượng lớn JavaScript.

Lighthouse cũng phát hiện vấn đề này trong quy trình kiểm tra các 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 tôi đã quay lại mã của mình và xoá cả phần JavaScript và CSS của thư viện đó.

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

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

Tất nhiên, điều này giúp điểm hiệu suất của chúng tôi tăng lên và Thời gian tương tác cũng được cải thiện đáng kể.

Tuy nhiên, đối với những thay đổi như thế này, chỉ kiểm tra các chỉ số và điểm số thôi là chưa đủ. Việc xoá mã thực tế luôn tiềm ẩn rủi ro, vì vậy, bạn nên luôn chú ý đến các lỗi hồi quy tiềm ẩn.

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

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 đã bị xoá

Vì vậy, nếu bạn xoá mã, hãy đảm bảo rằng bạn có một quy trình kiểm thử phù hợp để giúp bạn tránh được các lỗi hồi quy hình ảnh tiềm ẩn.

Tránh tải trọng mạng quá lớn

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

Lighthouse có thể phát hiện ra rằng chúng tôi 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 lớn
Hình 8. Phát hiện tải trọng mạng lớn

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

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

Như người ta thường nói: yêu cầu nhanh nhất là yêu cầu không được thực hiện.

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à đưa ra quyết định về việc liệu có nên cung cấp thành phần đó trong trải nghiệm ban đầu hay không. Vì đôi khi các tài nguyên này có thể bị trì hoãn, tải chậm hoặc được xử lý trong thời gian rảnh.

Trong trường hợp của chúng tôi, vì phải xử lý nhiều gói JavaScript, nên chúng tôi 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 tôi bắt đầu bằng trình phân tích gói webpack.Trình phân tích này cho chúng tôi biết rằng chúng tôi đang bao gồm một phần phụ thuộc có tên là unicode, tức là 1,6 MB JavaScript được phân tích cú pháp, vì vậy, khá nhiều.

Sau đó, chúng tôi chuyển sang trình chỉnh sửa và sử dụng Import Cost Plugin cho Visual code để có thể hình dung chi phí của mọi mô-đun mà chúng tôi đang nhập. Nhờ vậy, chúng tôi có thể khám phá thành phần nào đang 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 là BundlePhobia. Đây là một công cụ cho phép bạn nhập tên của bất kỳ gói NPM nào và xem kích thước ước tính sau khi rút gọn và nén bằng gzip. 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ỉ nặng 2,2 kb, vì vậy chúng tôi đã chuyển sang 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. Nhờ thay đổi này và việc khám phá ra những cơ hội khác để giảm kích thước gói JavaScript, chúng tôi đã tiết kiệm được 2,1 MB mã.

Chúng tôi nhận thấy hiệu suất tổng thể cải thiện 65% khi bạn tính đến kích thước nén và rút gọn của các gói này. Và chúng tôi nhận thấy rằng đây thực sự là một quy trình đáng thực hiện.

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

Giảm thời gian khởi động JavaScript bằng cách 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ó một điều khác có thể có tác động thực sự lớn, và đó là JavaScript.

JavaScript là tài nguyên tốn kém nhất. Trên thiết bị di động, nếu bạn gửi các gói JavaScript lớn, thì việc 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ì thực sự xảy ra. Vì vậy, chúng ta cần hiểu lý do JavaScript tốn nhiều tài nguyên đến 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, 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 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 chiếc điện thoại di động trung bình, quá trình này có thể mất từ 5 đến 10 lần. Đây là nguyên nhân làm chậm khả năng tương tác, vì vậy, chúng ta cần cố gắng giảm thiểu thời gian này.

Để giúp bạn phát hiện những vấn đề này trong ứng dụng, chúng tôi đã ra mắt một quy trình kiểm tra thời gian khởi động JavaScript mới 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, công cụ này cho chúng tôi biết rằng chúng tôi đã mất 1,8 giây để khởi động JavaScript. Điều đang 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 tính năng phân tách mã.

Tách mã giống như pizza

Tách mã là khái niệm thay vì cung cấp cho người dùng toàn bộ JavaScript, bạn chỉ cung cấp cho họ một phần tại một thời điểm khi họ cần.

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. Công cụ 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 đã kết 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ã chậm không đồng bộ khi cần.

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

Điều này không chỉ giúp giảm kích thước của các gói mà còn 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ã mà người dùng 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

Trò đùa về hiệu suất tải hình ảnh

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

Chúng tôi 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ể thu được một số lợi ích từ việc sử dụng các định dạng hình ảnh khác.

Kiểm tra hình ảnh
Hình 14. Kiểm tra hình ảnh bằng Lighthouse

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

Để 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, bằng các thư viện như imagemin.

Bằng cách này, bạn có thể đảm bảo rằng những hình ảnh được thêm vào trong tương lai sẽ tự động được 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ể chỉ cần lưu trữ hình ảnh của mình trên các dịch vụ đó.

Nếu bạn không muốn làm việc đó vì chi phí hoặc vấn đề về độ trễ, thì 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á

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

Việc lặp lại quy trình này cho nhiều hình ảnh trên trang web đã giúp chúng tôi giảm đáng kể quy mô trang tổng thể.

Sử dụng đúng định dạng cho nội dung ảnh động

Ảnh GIF có thể rất tốn kém. Đáng ngạc nhiên là định dạng GIF ban đầu không được thiết kế để làm nền tảng cho ả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ể kích thước tệp.

Trong ứng dụng Oodle, chúng tôi đã sử dụng ảnh GIF làm chuỗi 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 nặng khoảng 7,3 MB, quá nặng đối 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ó hai tệp nguồn – một tệp mp4 và một tệp WebM để hỗ trợ nhiều trình duyệt hơn.

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

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

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

Chúng tôi đã giảm được hơn 80% tổng trọng lượng nhờ quá trình chuyển đổi này. Điều này giúp giảm kích thước xuống còn khoảng 1 MB.

Tuy nhiên, 1 MB 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 API Loại hiệu quả để nhận ra rằng họ đang sử dụng 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 thời gian khứ hồi hiệu quả và giá trị tải xuống để ước tính loại mạng mà người dùng đang sử dụng. API 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 trang web vẫn có thể sử dụng được khi 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, ngay cả khi người dùng không thể nhìn thấy chúng ngay 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 Công cụ cho nhà phát triển. Nếu bạn thấy nhiều hình ảnh sắp xuất hiện trong khi chỉ có một vài hình ảnh hiển thị trên trang, thì có thể bạn nên cân nhắc sử dụng tính năng tải chậm.

Tính năng tải chậm chưa được hỗ trợ sẵn 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 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 là một thư viện 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ị để mang lại trải nghiệm tối ưu cho người dùng. Thư viện này cũng cung cấp một chế độ tích hợp không bắt buộc của IntersectionObserver, giúp bạn tra cứu chế độ hiển thị một cách hiệu quả.

Sau khi thay đổi, 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 byte nào được gửi đến trình duyệt cũng 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 xem nên tìm nạp nội dung nào trước. Vì vậy, đôi khi 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 ta, với tư cách là tác giả của trang, thông báo cho trình duyệt biết điều gì thực sự quan trọng đối với chúng ta. Rất may là trong vài năm qua, các nhà cung cấp trình duyệt đã bổ sung một số tính năng để giúp chúng ta thực hiện việc này, ví dụ: gợi ý tài nguyên như link rel=preconnect, hoặc 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 đúng nội dung vào đúng thời điểm, đồng thời 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 cách Lighthouse hướng dẫn chúng ta sử dụng một số tính năng này một cách hiệu quả.

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

Tránh nhiều lượt khứ hồi tốn kém đến bất kỳ nguồn gốc nào
Hình 17. Tránh nhiều lượt 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ự đang sử dụng rất nhiều Google Fonts. Bất cứ khi nào bạn thả một biểu định kiểu Google Fonts vào trang của mình, biểu định kiểu đó sẽ kết nối tối đa hai miền con. Và Lighthouse cho chúng ta biết rằng nếu có thể khởi động kết nối đó, chúng ta có thể tiết kiệm tới 300 mili giây trong thời gian kết nối ban đầu.

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

Đặc biệt là với một dịch vụ như Google Fonts, nơi CSS kiểu phông chữ của chúng tôi được lưu trữ trên googleapis.com và tài nguyên phông chữ của chúng tôi được lưu trữ trên Gstatic, điều này có thể có tác động rất 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 được 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ự rất mạnh mẽ, nó thông báo cho trình duyệt biế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 yêu cầu 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ữ web chính, vì chúng ta đang tải hai phông chữ web.

Việc tải trước 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ố tải, chẳng hạn như woff2.

Điều này có thể ảnh hưởng khá rõ rệt đế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 thuộc tính rel preload của phần tử link, nếu phông chữ web là yếu tố quan trọng đối với trang của bạn, thì trình duyệt trước tiên phải tìm nạp HTML, phân tích cú pháp CSS và sau đó, trình duyệt sẽ tìm nạp phông chữ web.

Khi sử dụng link rel preload, 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 ứng dụng của chúng tôi, việc này có thể giảm bớt một giây thời gian cần thiết để hiển thị văn bản bằng phông chữ web.

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

URL của Google Fonts mà chúng tôi chỉ định trên các kiểu phông chữ trong biểu định kiểu của mình là những URL 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 thường xuyên, vì vậy, nếu muốn có toàn quyền kiểm soát trải nghiệm tải phông chữ, bạn nên tự lưu trữ phông chữ web của mình. Điều này có thể rất hữu ích vì nó cho phép bạn truy cập vào những thứ như tải trước rel liên kết.

Trong trường hợp của chúng tôi, chúng tôi nhận thấy công cụ Google Web Fonts Helper thực sự hữu ích trong việc giúp chúng tôi ngoại tuyến một số phông chữ web đó và thiết lập chúng cục bộ, vì vậy hãy xem công cụ đó.

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

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

Hôm nay, chúng tôi có một 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, cũng như tải trước, chúng tôi còn đang phát triển một tính năng hoàn toàn mới cho trình duyệt thử nghiệm mà chúng tôi gọi là gợi ý ưu tiên.

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

Đây là một tính năng mới cho phép bạn gợi ý cho trình duyệt mức độ quan trọng của một tài nguyên. Tính năng này cho thấy một thuộc tính mới – tầm 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 ta 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, hình ảnh không quan trọng hoặc lệnh gọi API tìm nạp để giảm tranh chấp. Chúng ta cũng có thể tăng mức độ ưu tiên cho 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, trình duyệt đã làm như sau: chúng tôi có băng chuyền hình ảnh này với tất cả hình tượng trưng và trình duyệt đã 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. Rất tiếc, những hình ảnh ở giữa băng chuyền lại là những hình ảnh 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 những hình ảnh nền đó ở mức rất thấp, còn hình ảnh nền trước ở mức rất cao. Điều này đã giúp cải thiện 2 giây trên mạng 3G chậm, đồng thời giúp chúng tôi tìm nạp và hiển thị những hình ảnh đó nhanh hơn. Vậy nên, đây là một trải nghiệm tích cực.

Chúng tôi hy vọng có thể ra mắt tính năng này trên Canary trong vài tuần tới, vì vậy, hãy chú ý theo dõi.

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

Kiểu chữ là yếu tố cơ bản để có thiết kế đẹp. Nếu đang sử dụng phông chữ web, bạn không nên chặn việc hiển thị văn bản và chắc chắn không nên hiển thị văn bản ẩn.

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

Tránh văn bản ẩn trong khi Phông chữ web đang tải
Hình 22. Tránh văn bản ẩn trong khi phông chữ web đang tải

Nếu tải phông chữ web bằng khối font-face, 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ữ web đó. Một số trình duyệt sẽ đợi tối đa 3 giây để tải phông chữ trước khi quay lại phông chữ hệ thống và cuối cùng sẽ hoán đổi phông chữ đó thành phông chữ đã tải xuống.

Chúng tôi đang cố gắng tránh văn bản ẩn này, vì vậy, trong trường hợp này, chúng tôi sẽ không thể thấy các hình tượng trưng 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 thực 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');
    }

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

Trong trường hợp này, chúng ta sẽ sử dụng thuộc tính font-display: swap. Hoán đổi cho phông chữ một khoảng thời gian chặn bằng 0 giây và 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 lúc để tải. Và nó sẽ hoán đổi một khi kiểu chữ có sẵn.

Trong trường hợp ứng dụng của chúng tôi, điều này rất tuyệt vời vì nó cho phép chúng tôi hiển thị một số văn bản có ý nghĩa rất sớm và chuyển sang phông chữ web khi đã 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 các trang web khác), hãy áp dụng một chiến lược tải phông chữ web hiệu quả.

Có rất nhiều tính năng của 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 bạn cũng nên xem kho lưu trữ Công thức phông chữ web của Zach Leatherman vì kho lưu trữ này thực sự rất hữu ích.

Giảm tập lệnh chặn hiển thị

Có những phần khác trong ứng dụng mà chúng ta 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 cho người dùng sớm hơn một chút.

Trên dải dòng thời gian của 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 không thể thực sự nhìn thấy bất kỳ nội dung nào.

Cơ hội giảm biểu định kiểu chặn hiển thị
Hình 24. Cơ hội giảm biểu định kiểu chặn hiển thị

Việc tải xuống và xử lý biểu định kiểu bên ngoài đang chặn tiến trình kết xuất của chúng tôi, khiến tiến trình này không thể tiếp tục.

Chúng ta có thể thử tối ưu hoá đường dẫn hiển thị 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 chúng vào HTML, trình duyệt có thể hiển thị chúng ngay lập tức mà không cần đợi biểu định kiểu bên ngoài đến.

Trong trường hợp của chúng tôi, chúng tôi đã sử dụng một mô-đun NPM có tên là Critical để chèn nội dung quan trọng vào index.html trong một bước xây 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 nhọc cho chúng tôi, nhưng vẫn còn một chút khó khăn để mô-đun này hoạt động trơn tru trên nhiều tuyến đường.

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ì có thể rất khó để giới thiệu loại mẫu này nếu bạn không lập kế hoạch cho cấu trúc giao diện ứng dụng ngay từ đầu.

Đây là lý do tại sao bạn cần phải cân nhắc hiệu suất ngay từ đầu. Nếu không thiết kế để đạt hiệu suất ngay từ đầu, thì bạn có nhiều khả năng sẽ gặp vấn đề khi thực hiện việc này sau đó.

Cuối cùng, rủi ro chúng tôi chấp nhận đã được đền đáp, chúng tôi đã thành công và ứng dụng bắt đầu phân phối nội dung sớm hơn nhiều, giúp cải thiện đáng kể thời gian hiển thị nội dung đầu tiên.

Kết quả

Đó là một danh sách dài các biện 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 bước tiến khá tốt về tốc độ. Tất cả những thay đổi này đều được thúc đẩy nhờ việc chúng tôi liên tục kiểm tra và làm theo báo cáo của 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 xem kho lưu trữ của chúng tôi, đặc biệt là các yêu cầu kéo (PR) được đưa vào đó.

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 mang đến cơ hội thú vị cho tương lai trong nhiều lĩnh vực. Chúng tôi hy vọng một ý tưởng sẽ khơi dậy nhiều thử nghiệm hơn trong tương lai, đó là dữ liệu thực tế có thể thực sự đị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 nhiều quyết định tuỳ ý về những gì người dùng có thể muốn hoặc cần, và do đó, những gì đáng được tìm nạp trước, tải trước hoặc lưu trước vào bộ nhớ đệm. Nếu đoán đúng, chúng tôi 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 thực sự có dữ liệu để giúp bạn tối ưu hoá hiệu quả hơn ngay hôm nay. Bằng cách 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ệ 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ề những tài nguyên mà 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 sẽ tránh lãng phí dữ liệu của người dùng bằng cách tìm nạp trước nội dung quá mức. 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 đó.

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ợ các thử nghiệm này, chúng tôi rất vui được công bố 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 việc khám phá cách sử dụng dữ liệu để cải thiện hiệu suất web và hơn thế nữa. 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 nhờ sự cộng tác của cộng đồng nguồn mở, Minko Gechev, Kyle Matthews của Gatsby, Katie Hempenius và một số người khác.

Hãy xem Guess.js và cho chúng tôi biết suy nghĩ của bạn.

Tóm tắt

Điểm số và chỉ số giúp 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.

Tất cả chúng ta đều từng gặp phải tình trạng tải trang chậm khi đang 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ới tốc độ tải cực nhanh.

Cải thiện hiệu suất là 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 Lighthouse, bạn có thể mang lại trải nghiệm tốt hơn và toàn diện hơn cho người dùng.

Đặc biệt cảm ơn: Ward Peeters, Minko Gechev, Kyle Mathews, Katie Hempenius, Dom Farolino, Yoav Weiss, Susie Lu, Yusuke Utsunomiya, Tom Ankers, Lighthouse và hình tượng trưng của Google.