Dễ dàng cải thiện hiệu suất web – phiên bản Google I/O 2018

Tại Google I/O 2018, chúng tôi đã giới thiệu một loạt các công cụ, thư viện và kỹ thuật tối ưu hoá giúp bạn cải thiện hiệu suất web dễ dàng hơn. Ở đây, chúng tôi giải thích về các tính năng này bằng ứng dụng The Oodles Theater. Chúng tôi cũng đề cập đến 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 đã khá bận rộn khi cố gắng tìm ra cách tăng tốc độ và hiệu suất của Web. Điều này dẫn đến việc ra đời 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 The 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

Mỗi năm, Internet lại càng trở nên nặng nề hơn. 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, chẳng hạn như độ trễ mạng, giới hạn CPU, các mẫu chặn hiển thị hoặc mã không cần thiết của bên thứ ba, 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à 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ể thực hiện nhiều thao tác cho đến khi trang tải xong. Bạn không thể lấy giá trị từ trang này, bạn không thể chiêm ngưỡng vẻ đẹp của trang.

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? (Tốc độ quan trọng, 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 đôi khi người dùng cảm thấy khó khăn khi tìm ra nơi bắt đầu tối ưu hoá. Rất may 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 làm việc về hiệu suất

Lighthouse là một phần của Công cụ cho nhà phát triển Chrome, 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 các quy trình kiểm tra hiệu suất mới. Đây là những quy trình thực sự hữu ích trong quy trình phát triển hằng ngày.

Các bài kiểm tra mới cho Lighthouse
Hình 2. Các bài 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 trong 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ể dùng thử một số Google Doodle 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 xây dựng ứng dụng, chúng tôi muốn đảm bảo rằng ứng dụng hoạt động hiệu quả nhất có thể. Điểm bắt đầu để 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 mà chúng tôi thấy trong báo cáo Lighthouse khá tệ. Trên mạng 3G, người dùng cần đợi 15 giây để có được nội dung hiển thị có ý nghĩa đầu tiên hoặc để ứng dụng có thể tương tác. Lighthouse đã làm nổi 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 giảm bớt dung lượng.

Đây là lúc chúng tôi bắt đầu thử thách hiệu suất đầu tiên: tìm những thứ 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á các 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. Giảm thiểu và nén JavaScript và CSS

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

Giảm thiểu là một nhiệm 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 tạo nào mà bạn sử dụng.

Một hoạt động 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 chưa 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 Firebase Hosting để lưu trữ mã của mình và Firebase cho phép nén gzip theo mặc định. Vì vậy, chỉ cần lưu trữ mã của mình trên một CDN hợp lý là chúng tôi đã có được tính năng này 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ư ZopfliBrotli cũng đang được sử dụng rộng rãi. Brotli được hỗ trợ trong hầu hết các trình duyệt và bạn có thể sử dụng một tệp nhị phân để nén trước các thành phần của mình trước khi gửi chúng đến máy chủ.

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

Bước tiếp theo của chúng tôi 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.

Quy trình kiểm tra Chính sách bộ nhớ đệm không hiệu quả trong Lighthouse đã giúp chúng tôi nhận thấy rằng mình có thể tối ưu hoá các chiến lược lưu vào bộ nhớ đệm để đạt được chính xác mục tiêu đó. Bằng cách đặt tiêu đề hết hạn max-age trong máy chủ của mình, chúng tôi đảm bảo rằng trong một lượt truy cập lặ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 cố gắng lưu vào bộ nhớ đệm càng nhiều tài nguyên càng tốt một cách an toàn trong khoảng thời gian dài nhất có thể và cung cấp mã thông báo xác thực để xác thực lại 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 lượt 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 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, nhóm của bạn hoặc cá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 sẽ bị bỏ lại. Đó chính xác là những gì đã xảy ra với chúng tôi.

Lúc đầu, chúng tôi đã sử dụng thư viện Thành phần Material Design để nhanh chóng tạo mẫu ứ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 mất thư viện đó. Rất may, chế độ kiểm tra mức độ bao phủ mã đã giúp chúng tôi tìm lại được thành phần này trong gói.

Bạn có thể kiểm tra số liệu thống kê về mức độ bao phủ mã trong DevTools, 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 2 sọc đỏ lớn trong ảnh chụp màn hình dưới cùng – chúng tôi đã không dùng đến hơn 95% CSS và một lượng lớn JavaScript.

Lighthouse cũng phát hiện vấn đề này trong quy tắc CSS không dùng đến. Kết quả cho thấy có thể tiết kiệm 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 giảm bộ chuyển đổi MVC, các kiểu của chúng ta sẽ giảm xuống 10 KB
Hình 6. Nếu chúng ta giảm 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 kích thước gói CSS của chúng tôi xuống 20 lần, một con số khá ấn tượng đối với một cam kết nhỏ chỉ có 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, đồng thời Thời gian tương tác cũng được cải thiện đáng kể.

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

Mã của chúng tôi không được dùng trong 95% trường hợp – vẫn còn 5% ở đâ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 các kiểu trong thư viện đó – những mũi tên nhỏ trong thanh trượt hình vẽ. Tuy nhiên, vì nó quá nhỏ nên chúng ta có thể chuyển sang và kết hợp các kiểu đó 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 đã bị xoá

Vì vậy, nếu bạn xoá mã, hãy đảm bảo rằng bạn đã thiết lập quy trình kiểm thử phù hợp để giúp bạn ngăn chặn các lỗi hiển thị 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à ả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 thấy 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 khổng lồ
Hình 8. Phát hiện tải trọng mạng khổng lồ

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

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

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.

Lý tưởng 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 những thành phần đó và quyết định xem có thực sự đáng để phân phối cùng với trải nghiệm ban đầu hay không. Vì đôi khi các thành phần này có thể bị hoãn lại hoặc 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ì chúng tôi đang xử lý rất nhiều gói JavaScript, nên chúng tôi 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 tôi bắt đầu bằng trình phân tích gói webpack, 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, có kích thước 1,6 MB của JavaScript đã 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 Trình bổ trợ chi phí nhập cho mã trực quan. Nhờ đó, chúng tôi có thể hình dung được chi phí của từng mô-đun mà chúng tôi đang nhập. Điều này giúp chúng tôi biết được thành phần nào đang bao gồm 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 tối thiểu và kích thước nén gzip ước tính của gói đó. Chúng tôi đã tìm thấy một lựa chọn 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 mô-đun đó.

Điều này ảnh hưởng lớn đến hiệu suất của chúng tôi. Giữa thay đổi này và việc khám phá các 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ể tăng 65% sau khi tính đến kích thước được nén và giảm thiểu của các gói này. Và chúng tôi nhận thấy rằng đây là một quy trình thực sự đáng làm.

Vì vậy, nói chung, hãy cố gắng loại bỏ những lượt tải không cần thiết trên các trang web và ứng dụng của bạn. Việc lập danh sách tài sản và đo lường tác độ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 tài sản của bạn một cách thường xuyên.

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ó một thứ khác có thể ảnh hưởng rất lớn, đó là JavaScript.

JavaScript là tài sản đắt giá nhất của bạn. 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 của bạn. Điều đó có nghĩa là họ có thể nhấn vào giao diện người dùng mà không có bất kỳ điều gì có ý nghĩa thực sự xảy ra. Vì vậy, chúng ta cần hiểu lý do khiến 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 hết, chúng ta phải tải tập lệnh đó xuống, chúng ta có một công cụ JavaScript sau đó cần phân tích cú pháp mã đó, cần biên dịch và thực thi mã đó.

Giờ đây, những 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à một chiếc đ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 lâu hơn. Đây là yếu tố làm chậm khả năng tương tác, vì vậy, điều quan trọng là chúng ta phải cố gắng giảm bớt yếu tố 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 đã giới thiệu 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

Trong trường hợp ứng dụng Oodle, ứng dụng này cho chúng ta biết rằng chúng ta đã dành 1,8 giây cho quá trình 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 đường 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 chia mã.

Phân chia 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 một lượng JavaScript tương đương với cả chiếc bánh pizza, thì sao nếu bạn chỉ cung cấp cho họ từng lát một khi họ cần?

Bạn có thể áp dụng tính năng phân chia mã ở cấp tuyến đường 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 đã kết hợp tính năng phân chia mã vào ứng dụng của mình, chúng tôi đã chuyển từ tính năng nhập tĩnh sang tính năng nhập động, cho phép chúng tôi tải mã một cách không đồng bộ khi cần.

Phân chia mã bằng tính năng nhập động
Hình 13. Phân chia mã bằng tính năng nhập linh động

Tác động của việc này là vừa giảm kích thước của cá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 chạy nhanh hơn 56%.

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

Tận dụng các khái niệm như phân chia mã, khám phá các ý tưởng như loại bỏ mã không dùng và xem kho lưu trữ webpack-libs-optimizations để biết một số ý tưởng về cách bạn có thể giảm kích thước thư viện nếu đ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 đang sử dụng rất nhiều hình ảnh. Rất tiếc, Lighthouse lại không hào hứng với điều này bằng chúng tôi. Trên thực tế, chúng tôi đã thất bại trong cả 3 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 điều chỉnh kích thước hình ảnh một cách chính xác và cũng có thể đạt đượ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. 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.

Đố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 hoá hơn là thêm bước tối ưu hoá hình ảnh vào quy trình tạo, 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 trong tương lai sẽ được tối ưu hoá tự động. Một số CDN (chẳng hạn như Akamai) hoặc các giải pháp của bên thứ ba (chẳng hạn 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 như vậy vì chi phí hoặc cá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á

PNG nền của chúng tôi được gắn cờ trong webpack là lớn và điều này là đúng. Sau khi điều chỉnh kích thướ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à kích thướ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 của chúng tôi đã giúp chúng tôi giảm đáng kể trọng lượng tổng thể của trang.

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

GIF có thể rất tốn kém. Điều đáng ngạc nhiên là định dạng GIF ban đầu không hề được dự định là một 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 đã sử dụng một GIF làm chuỗi giới thiệu trên trang chủ. Theo Lighthouse, chúng tôi có thể tiết kiệm hơn 7 MB bằng cách chuyển sang một đị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á lớn đối với bất kỳ trang web hợp lý nào.Vì vậy, thay vào đó, chúng tôi đã chuyển đoạn video này 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 GIF ảnh động thành tệp mp4. Định dạng WebM giúp bạn tiết kiệm dung lượng hơn nữa – API ImageOptim 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ờ việc chuyển đổi này. Điều này giúp chúng tôi 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 để truyền qua mạng, đặc biệt là đối với người dùng có băng thông hạn chế. Rất may là chúng tôi có thể dùng Effective Type API để nhận ra rằng họ đang dùng băng thông thấp 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à các giá trị giảm để ước tính loại mạng mà người dùng đang sử dụng. Hàm 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 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) { ... }

Việc này có thể làm giảm trải nghiệm một chút, nhưng ít nhất thì trang web vẫn 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 quá dài thường tải hình ảnh, ngay cả khi người dùng không thấy ngay các 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 Công cụ cho nhà phát triển. Nếu thấy nhiều hình ảnh đến trong khi chỉ có một vài hình ảnh xuất hiện trên trang, thì có nghĩa là bạn có thể cân nhắc việc tải chúng một cách trì hoãn.

Tính năng tải từng phần chưa được hỗ trợ nguyên bản trong trình duyệt, vì vậy chúng ta phải sử dụng JavaScript để thêm khả năng này. Chúng tôi đã sử dụng thư viện Lazysizes để thêm hành vi tải từng phần vào các trang bìa củ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ề khả năng 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 lựa chọn tích hợp IntersectionObserver (không bắt buộc), giúp bạn tra cứu khả năng hiển thị một cách 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 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 nên tìm nạp nội dung nào trước. Vì vậy, đôi khi chúng 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 đã thêm một số tính năng để hỗ trợ chúng ta trong việc này, chẳng hạn như gợi ý về tài nguyên như link rel=preconnect, hoặc preload hay 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 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 cách Lighthouse hướng dẫn chúng ta sử dụng hiệu quả một số tính năng này.

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

Tránh nhiều chuyến đi khứ hồi tốn kém đến bất kỳ điểm xuất phát nào
Hình 17. Tránh nhiều chuyến bay khứ hồi tốn kém đến bất kỳ nơi nào

Trong trường hợp ứng dụng Oodle, chúng tôi thực sự đang sử dụng rất nhiều phông chữ của Google. Bất cứ khi nào bạn thả một biểu định kiểu Phông chữ của Google vào trang của mình, trang đó sẽ kết nối tối đa 2 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 đượ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 kết nối trước rel liên kết, chúng ta có thể che giấu hiệu quả độ trễ kết nối đó.

Đặc biệt là với những thứ như Google Fonts, nơi CSS của kiểu chữ được lưu trữ trên googleapis.com và tài nguyên phông chữ đượ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 tải trước các yêu cầu chính.

Tải trước các yêu cầu về khoá
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 một tài nguyên là cần thiết 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 đó càng sớm càng tốt.

Giờ đây, Lighthouse cho biết 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 2 phông chữ web.

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

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

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

Tuy nhiên, nếu bạn định thử tải trước phông chữ bằng Google Fonts, thì có một điểm cần lưu ý.

Các URL của Phông chữ Google mà chúng tôi chỉ định trên các mặt 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 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ữ trên web. Đ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 rất hữu ích trong việc giúp chúng tôi ngắt kết nối một số phông chữ web đó và thiết lập chúng cục bộ. Vì vậy, hãy dùng thử công cụ đó.

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

Thử nghiệm: Gợi ý ư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 ý về tài nguyên và 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 ở giai đoạn thử nghiệm, đó là gợi ý về 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 ý 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 biết mức độ quan trọng của một tài nguyên. Tính năng này hiển thị 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 ta 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ư các kiểu, hình ảnh không quan trọng hoặc lệnh gọi API tìm nạp để giảm sự tranh chấp. Chúng ta cũng có thể tăng mức độ ưu tiên của những thứ quan trọng hơn, chẳng hạn như hình ảnh chính.

Trong trường hợp ứng dụng Oodle của chúng tôi, điều này thực sự dẫn đến một nơi thực tế mà chúng tôi 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 đã thực hiện thao tác sau: chúng tôi có băng chuyền hình ảnh này với tất cả các hình hoạ của mình 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. 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, hình ảnh nền trước ở mức rất cao. Điều này đã có tác động trong 2 giây đối với mạng 3G chậm, cũng như tốc độ chúng tôi có thể tìm nạp và hiển thị những hình ảnh đó. Vì vậy, đây là một trải nghiệm tích cực và thú vị.

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ữ trên 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ữ trên web, bạn không nên chặn quá trình hiển thị văn bản và chắc chắn không nên hiển thị văn bản vô hình.

Giờ đây, chúng tôi làm nổi bật điều này trong Lighthouse bằng chế độ kiểm tra tránh văn bản không nhìn thấy được trong khi phông chữ web đang tải.

Tránh văn bản không hiển thị trong khi Phông chữ 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 phông chữ, bạn đang cho phép trình duyệt quyết định việc cần làm nếu phông chữ trên web đó mất nhiều thời gian để tìm nạp. Một số trình duyệt sẽ đợi tối đa 3 giây cho việc này trước khi quay lại phông chữ hệ thống và cuối cùng sẽ thay thế phông chữ đó bằng phông chữ sau khi 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ể xem các hình vẽ cổ điển của tuần này nếu phông chữ web mất quá nhiều thời gian để tải. 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ữ trên web sẽ hiển thị hoặc dự phòng dựa trên thời gian cần thiết để chúng hoán đổi.

Trong trường hợp này, chúng ta đang sử dụng tính năng hoán đổi hiển thị phông chữ. Swap (Hoán đổi) cho kiểu chữ một khoảng thời gian chặn là 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 một 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 khi có phông chữ.

Trong trường hợp ứng dụng của chúng tôi, lý do khiến việc này hiệu quả 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ữ trên 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ữ trên web (như phần lớn trang web), hãy áp dụng một chiến lược tải phông chữ trên 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ữ Web Font Recipes (Công thức phông chữ trên web) của Zach Leatherman vì kho lưu trữ này rất hữu ích.

Giảm các 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 lên 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 này, khi tất cả các tài nguyên đang tải, người dùng không thực sự thấy nội dung nào.

Giảm cơ hội cho biểu định kiểu chặn hiển thị
Hình 24. Giảm cơ hội cho 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 ngăn quá trình kết xuất của chúng tôi tiến hành bất kỳ bước nào.

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 quá trình kết xuất ban đầu này và nội tuyến chúng trong HTML, thì trình duyệt có thể kết xuất chúng ngay lập tức mà không cần đợi biểu định kiểu bên ngoài xuất hiệ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 để nội tuyến nội dung quan trọng trong index.html trong bước tạo.

Mặc dù mô-đun này đã giúp chúng tôi giải quyết hầu hết các vấn đề, nhưng vẫn có 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 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ể gặp khó khăn khi giới thiệu loại mẫu này nếu không lên kế hoạch cho cấu trúc vỏ ứng dụng ngay từ đầu.

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

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

Kết quả

Đó là một danh sách dài các hoạt động 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 cỡ trung 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 hiện nhờ việc 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 thoải mái xem kho lưu trữ của chúng tôi, đặc biệt là các 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 nhiều cơ hội thú vị cho tương lai ở nhiều lĩnh vực. Một ý tưởng mà chúng tôi hy vọng sẽ thúc đẩy nhiều thử nghiệm hơn trong tương lai là dữ liệu thực có thể thực sự hướng dẫn nhữ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 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.

Hiện tại, chúng tôi có dữ liệu để đưa ra thông tin chính xác hơn cho việc tối ưu hoá. Bằng cách sử dụng API Báo cáo của Google Analytics, chúng ta có thể xem xét trang tiếp theo và tỷ lệ phần trăm thoát cho mọi URL 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ải trước nội dung một cách quá mức. Chúng ta có thể tận dụng dữ liệu đó của Google Analytics và sử dụng mô hình học máy 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. Nhóm theo hướng dữ liệu cho ứng dụng web

Để tạo điều kiện thuận lợi cho các thử nghiệm này, chúng tôi rất vui mừng được thông báo về một sáng kiến mới mà chúng tôi gọi 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á 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. Thư viện này được xây dựng với sự cộng tác của cộng đồng nguồn mở bởi Minko Gechev, Kyle Matthews của 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 mục tiêu.

Chắc hẳn ai cũng từng gặp phải tình trạng trang tải 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 thực sự 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 những 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 đến trải nghiệm tốt hơn và toàn diện hơn cho người dùng.

Xin chân thành cảm ơn: Ward Peeters, Minko Gechev, Kyle Mathews, Katie Hempenius, Dom Farolino, Yoav Weiss, Susie Lu, Yusuke Utsunomiya, Tom Ankers, Lighthouse và Google Doodles.