Vì chúng tôi xây dựng các trang web chủ yếu phụ thuộc vào JavaScript, nên đôi khi chúng tôi phải trả tiền cho những gì chúng tôi gửi đi theo cách mà không phải lúc nào chúng tôi cũng có thể dễ dàng thấy được. Trong bài viết này, chúng tôi sẽ giải thích lý do bạn cần có kỷ luật một chút nếu muốn trang web tải và tương tác nhanh trên thiết bị di động. Việc phân phối ít JavaScript hơn có thể đồng nghĩa với việc tốn ít thời gian truyền mạng hơn, tốn ít thời gian giải nén mã hơn và tốn ít thời gian hơn để phân tích cú pháp và biên dịch JavaScript này.
Mạng
Khi nghĩ về chi phí của JavaScript, hầu hết các nhà phát triển đều nghĩ về chi phí tải xuống và thực thi. Việc gửi thêm byte JavaScript qua đường dây sẽ mất nhiều thời gian hơn để kết nối của người dùng chậm hơn.
Đây có thể là một vấn đề, do loại kết nối mạng hiệu quả mà người dùng có thể không thực sự là 3G, 4G hoặc Wi-Fi. Bạn có thể sử dụng Wi-Fi của quán cà phê nhưng được kết nối với điểm phát sóng di động có tốc độ 2G.
Bạn có thể giảm chi phí chuyển mạng của JavaScript thông qua:
- Chỉ gửi mã mà người dùng cần.
- Sử dụng tính năng phân tách mã để chia JavaScript thành những gì quan trọng và không quan trọng. Các gói mô-đun như gói web hỗ trợ phân tách mã.
- Tải từng phần một đoạn mã không quan trọng.
- Giảm thiểu
- Dùng UglifyJS để giảm thiểu mã ES5.
- Sử dụng babel-minify hoặc uglify-es để rút gọn ES2015+.
- Nén
- Xoá đoạn mã không dùng đến.
- Xác định các cơ hội để mã có thể bị xoá hoặc tải từng phần nhờ mức độ sử dụng mã của DevTools.
- Sử dụng babel-preset-env và danh sách trình duyệt để tránh việc chuyển đổi các tính năng đã có trong các trình duyệt hiện đại. Các nhà phát triển nâng cao có thể thấy bản phân tích kỹ lưỡng về các gói webpack của họ giúp xác định các cơ hội cắt giảm những phần phụ thuộc không cần thiết.
- Để loại bỏ mã, hãy xem các tính năng tối ưu hoá nâng cao của tree-shaking (lắc cây), close Compilerr (Trình biên dịch đóng) và các trình bổ trợ cắt thư viện như lodash-babel-plugin hoặc ContextReplacementPlugin của webpack cho các thư viện như Moment.js.
- Lưu mã vào bộ nhớ đệm để giảm thiểu các chuyến đi mạng.
- Sử dụng tính năng lưu vào bộ nhớ đệm HTTP để đảm bảo trình duyệt lưu phản hồi vào bộ nhớ đệm một cách hiệu quả. Xác định thời gian hoạt động tối ưu cho các tập lệnh (max-age) và mã thông báo xác thực nguồn cung (ETag) để tránh chuyển các byte không thay đổi.
- Việc lưu vào bộ nhớ đệm của Trình chạy dịch vụ có thể giúp mạng ứng dụng của bạn có khả năng phục hồi và cho phép bạn truy cập nhanh hơn vào các tính năng như bộ nhớ đệm mã của V8.
- Sử dụng chức năng lưu vào bộ nhớ đệm trong thời gian dài để tránh phải tìm nạp lại các tài nguyên không thay đổi. Nếu sử dụng Webpack, hãy xem phần băm tên tệp.
Phân tích cú pháp/Biên dịch
Sau khi tải xuống, một trong những chi phí nặng nhất của JavaScript là thời gian để công cụ JS phân tích cú pháp/biên dịch mã này. Trong Công cụ phát triển Chrome, phân tích cú pháp và biên dịch là một phần của thời gian "Tập lệnh" màu vàng trong bảng điều khiển Hiệu suất.
Các tab Từ dưới lên và Cây cuộc gọi hiển thị cho bạn thời gian Phân tích cú pháp/biên dịch chính xác:
Nhưng tại sao điều này lại quan trọng?
Việc dành một thời gian dài để phân tích cú pháp/biên dịch mã có thể làm chậm trễ đáng kể thời gian người dùng có thể tương tác với trang web của bạn. Bạn gửi càng nhiều JavaScript thì càng mất nhiều thời gian để phân tích cú pháp và biên dịch JavaScript trước khi trang web của bạn tương tác được.
Theo byte cho byte, JavaScript cho trình duyệt xử lý tốn kém hơn so với hình ảnh có kích thước tương đương hoặc phông chữ web — Tom Dale
So với JavaScript, có rất nhiều chi phí liên quan đến việc xử lý hình ảnh có kích thước tương đương (chúng vẫn phải được giải mã!) nhưng trên phần cứng di động thông thường, JS có nhiều khả năng tác động tiêu cực đến khả năng tương tác của trang hơn.
Khi chúng tôi nói về việc phân tích cú pháp và biên dịch chậm; ngữ cảnh rất quan trọng — chúng tôi đang nói về điện thoại di động trung bình ở đây. Người dùng trung bình có thể sở hữu điện thoại có CPU và GPU chậm, không có bộ nhớ đệm L2/L3 và thậm chí có thể bị hạn chế về bộ nhớ.
Khả năng của mạng và khả năng của thiết bị không phải lúc nào cũng phù hợp. Người dùng có kết nối Fiber tuyệt vời không nhất thiết phải có CPU tốt nhất để phân tích cú pháp và đánh giá JavaScript được gửi đến thiết bị của họ. Điều này cũng đúng khi đảo ngược... một kết nối mạng rất tệ nhưng CPU lại cực nhanh. – Kristofer Baxter, LinkedIn
Dưới đây, chúng ta có thể xem chi phí phân tích cú pháp ~1MB JavaScript đã giải nén (đơn giản) trên phần cứng thấp và cao cấp. Thời gian phân tích/biên dịch mã chênh lệch gấp 2-5 lần giữa các điện thoại nhanh nhất trên thị trường và các điện thoại thông thường.
Còn một trang web thực tế như CNN.com thì sao?
Trên iPhone 8 cao cấp, bạn chỉ mất khoảng 4 giây để phân tích cú pháp/biên dịch JS của CNN so với khoảng 13 giây đối với một điện thoại thông thường (Moto G4). Điều này có thể ảnh hưởng đáng kể đến tốc độ mà người dùng có thể tương tác đầy đủ với trang web này.
Điều này nêu bật tầm quan trọng của việc kiểm thử trên phần cứng trung bình (như Moto G4) thay vì chỉ trên điện thoại có thể nằm trong túi của bạn. Tuy nhiên, ngữ cảnh vẫn quan trọng: tối ưu hoá cho điều kiện thiết bị và mạng mà người dùng của bạn gặp phải.
Chúng tôi có thực sự đang gửi xuống quá nhiều JavaScript không? Ồ, có thể là :)
Khi sử dụng Kho lưu trữ HTTP (khoảng 500 nghìn trang web hàng đầu) để phân tích trạng thái của JavaScript trên thiết bị di động, chúng tôi có thể thấy rằng 50% trang web mất hơn 14 giây để tương tác. Các trang web này dành tối đa 4 giây để phân tích cú pháp và biên dịch JS.
Hãy tính đến thời gian cần thiết để tìm nạp và xử lý JS cũng như các tài nguyên khác. Không có gì đáng ngạc nhiên khi người dùng có thể phải đợi một lúc trước khi cảm thấy các trang đã sẵn sàng sử dụng. Chắc chắn là chúng tôi có thể làm tốt hơn ở đây.
Việc xoá JavaScript không quan trọng khỏi các trang của bạn có thể làm giảm thời gian truyền, việc phân tích cú pháp và biên dịch tốn nhiều CPU cũng như giảm chi phí bộ nhớ tiềm ẩn. Tính năng này cũng giúp các trang của bạn tương tác nhanh hơn.
Thời gian thực thi
Việc này không chỉ giúp phân tích cú pháp và biên dịch mà có thể gây tốn kém. Thực thi JavaScript (chạy mã sau khi được phân tích cú pháp/biên dịch) là một trong những thao tác phải diễn ra trên luồng chính. Thời gian thực thi dài cũng có thể cho biết thời điểm người dùng có thể tương tác với trang web của bạn.
Nếu tập lệnh thực thi trong hơn 50 mili giây, thì thời gian tương tác sẽ bị trì hoãn bởi toàn bộ khoảng thời gian cần thiết để tải xuống, biên dịch và thực thi JS — Alex Russell
Để giải quyết vấn đề này, JavaScript được hưởng lợi từ việc phân thành các phần nhỏ để tránh bị khoá luồng chính. Tìm hiểu xem bạn có thể giảm lượng công việc đang thực hiện trong quá trình thực thi hay không.
Các chi phí khác
JavaScript có thể tác động đến hiệu suất của trang theo những cách khác:
- Bộ nhớ. Các trang có thể thường xuyên bị giật hoặc tạm dừng do GC (thu gom rác). Khi một trình duyệt thu hồi bộ nhớ, quá trình thực thi JS sẽ tạm dừng để một trình duyệt thường xuyên thu thập rác có thể tạm dừng việc thực thi thường xuyên hơn mức chúng ta mong muốn. Tránh hiện tượng rò rỉ bộ nhớ và tạm dừng gc thường xuyên để đảm bảo các trang không bị giật.
- Trong thời gian chạy, JavaScript chạy trong thời gian dài có thể chặn luồng chính gây ra các trang không phản hồi. Việc chia nhỏ công việc thành các phần nhỏ (sử dụng
requestAnimationFrame()
hoặcrequestIdleCallback()
để lên lịch) có thể giảm thiểu các vấn đề về khả năng phản hồi, nhờ đó giúp cải thiện Hoạt động tương tác với nội dung hiển thị tiếp theo (INP).
Các mô hình để giảm chi phí phân phối JavaScript
Khi bạn đang cố gắng duy trì thời gian phân tích cú pháp/biên dịch và truyền mạng cho JavaScript ở mức chậm, có một số mẫu có thể giúp ích như phân đoạn dựa trên tuyến hoặc PRPL.
PRPL (Đạo luật bảo vệ quyền riêng tư của trẻ em)
PRPL (Đẩy, Kết xuất, Trước bộ nhớ đệm, Tải từng phần) là một mẫu tối ưu hoá khả năng tương tác thông qua tính năng phân tách mã và lưu vào bộ nhớ đệm linh hoạt:
Hãy hình dung tác động của quy trình này.
Chúng tôi phân tích thời gian tải của các trang web dành cho thiết bị di động phổ biến và Ứng dụng web tiến bộ bằng cách sử dụng Số liệu thống kê cuộc gọi trong thời gian chạy của V8. Như chúng ta có thể thấy, thời gian phân tích cú pháp (hiển thị bằng màu cam) là một phần quan trọng mà nhiều trang web trong số này dành thời gian:
Wego (một trang web sử dụng PRPL) cố gắng duy trì thời gian phân tích cú pháp thấp cho các tuyến của chúng và tương tác rất nhanh. Nhiều trang web khác ở trên đã sử dụng phân tách mã và ngân sách hiệu suất để cố gắng giảm chi phí JS.
Tự thân khởi nghiệp tăng tiến
Nhiều trang web tối ưu hoá khả năng hiển thị nội dung với cái giá là tương tác cao. Để có được lần vẽ đầu tiên nhanh khi bạn có các gói JavaScript lớn, đôi khi, nhà phát triển sử dụng tính năng kết xuất phía máy chủ; sau đó "nâng cấp" nó để đính kèm trình xử lý sự kiện khi JavaScript cuối cùng được tìm nạp.
Hãy cẩn thận — việc này cũng có chi phí riêng. Bạn 1) thường gửi xuống một phản hồi HTML lớn hơn có thể đẩy tính tương tác của chúng tôi, 2) có thể khiến người dùng rơi vào một thung lũng kỳ lạ nơi một nửa trải nghiệm không thể thực sự tương tác được cho đến khi JavaScript hoàn tất xử lý.
Phương pháp tự thân khởi nghiệp tăng dần có thể là một phương pháp hiệu quả hơn. Gửi một trang có ít chức năng nhất (chỉ bao gồm HTML/JS/CSS cần thiết cho tuyến đường hiện tại). Khi có thêm tài nguyên, ứng dụng có thể tải từng phần và mở khoá thêm nhiều tính năng.
Tải mã tương ứng với nội dung mà bạn nhìn thấy. PRPL và Kiểu tự khởi động tiến bộ là các mẫu có thể giúp thực hiện việc này.
Kết luận
Kích thước truyền rất quan trọng đối với các mạng cấp thấp. Thời gian phân tích cú pháp là rất quan trọng đối với các thiết bị ràng buộc CPU. Duy trì những vấn đề thấp này.
Các nhóm đã gặt hái được thành công khi áp dụng ngân sách hiệu suất nghiêm ngặt để giảm thiểu thời gian truyền và phân tích cú pháp/biên dịch JavaScript. Xem video "Bạn có thể mua đủ không? " của Alex Russell: Ngân sách hiệu suất web trong thực tế" để được hướng dẫn về ngân sách cho thiết bị di động.
Nếu bạn đang xây dựng một trang web nhắm đến thiết bị di động, hãy cố gắng hết sức để phát triển trên phần cứng đại diện, duy trì thời gian phân tích/biên dịch JavaScript ở mức thấp và sử dụng Ngân sách hiệu suất để đảm bảo nhóm của bạn có thể theo dõi chi phí cho JavaScript.
Tìm hiểu thêm
- Hội nghị Nhà phát triển Chrome 2017 – Các phương pháp hay nhất về quá trình tải hiện đại
- Hiệu suất khởi động JavaScript
- Giải quyết khủng hoảng hiệu suất web — Nolan Lawson
- Bạn có đủ khả năng chi trả không? Ngân sách hiệu suất thực tế – Alex Russell
- Đánh giá khung web và thư viện – Kristofer Baxter
- Kết quả thử nghiệm của Cloudflare với Brotli để nén (lưu ý rằng Brotli động ở chất lượng cao hơn có thể làm chậm trễ quá trình kết xuất trang ban đầu, vì vậy, hãy đánh giá cẩn thận. Thay vào đó, bạn nên nén tĩnh.)
- Hợp đồng tương lai hiệu suất – Sam Saccone