Tìm hiểu tầm nhìn đằng sau thành phần Tập lệnh của Next.js, cung cấp giải pháp tích hợp sẵn để tối ưu hoá việc tải tập lệnh của bên thứ ba.
Khoảng 45% yêu cầu từ các trang web được phân phát trên thiết bị di động và máy tính là yêu cầu của bên thứ ba, trong đó 33% là tập lệnh. Kích thước, độ trễ và tốc độ tải của tập lệnh bên thứ ba có thể ảnh hưởng đáng kể đến hiệu suất của trang web. Thành phần Tập lệnh Next.js đi kèm với các phương pháp hay nhất và chế độ mặc định để giúp nhà phát triển đưa tập lệnh của bên thứ ba vào ứng dụng của họ, đồng thời giải quyết các vấn đề tiềm ẩn về hiệu suất ngay từ đầu.
Tập lệnh của bên thứ ba và tác động của các tập lệnh đó đến hiệu suất
Tập lệnh của bên thứ ba cho phép nhà phát triển web tận dụng các giải pháp hiện có để triển khai các tính năng phổ biến và giảm thời gian phát triển. Tuy nhiên, những người tạo ra các tập lệnh này thường không có động lực để xem xét tác động về hiệu suất đối với trang web sử dụng. Các tập lệnh này cũng là một hộp đen đối với những nhà phát triển sử dụng chúng.
Tập lệnh chiếm một số lượng đáng kể byte của bên thứ ba được các trang web tải xuống trên nhiều danh mục yêu cầu của bên thứ ba. Theo mặc định, trình duyệt sẽ ưu tiên các tập lệnh dựa trên vị trí của chúng trong tài liệu. Điều này có thể làm chậm quá trình khám phá hoặc thực thi các tập lệnh quan trọng đối với trải nghiệm người dùng.
Bạn nên tải sớm các thư viện bên thứ ba cần thiết cho bố cục để kết xuất trang. Bạn nên trì hoãn các bên thứ ba không cần thiết cho quá trình kết xuất ban đầu để không chặn các quá trình xử lý khác trên luồng chính. Lighthouse có hai quy trình kiểm tra để gắn cờ các tập lệnh chặn hiển thị hoặc chặn luồng chính.
Bạn cần xem xét trình tự tải tài nguyên của trang để không làm chậm trễ các tài nguyên quan trọng và không để các tài nguyên không quan trọng chặn các tài nguyên quan trọng.
Mặc dù có các phương pháp hay nhất để giảm tác động của bên thứ ba, nhưng không phải ai cũng biết cách triển khai các phương pháp đó cho mọi bên thứ ba mà họ sử dụng. Điều này có thể phức tạp vì:
- Trung bình, các trang web sử dụng 21 đến 23 bên thứ ba khác nhau (bao gồm cả tập lệnh) trên thiết bị di động và máy tính. Cách sử dụng và đề xuất có thể khác nhau đối với mỗi loại.
- Cách triển khai nhiều bên thứ ba có thể khác nhau tuỳ thuộc vào việc có sử dụng một khung hoặc thư viện giao diện người dùng cụ thể hay không.
- Các thư viện mới hơn của bên thứ ba thường xuyên được ra mắt.
- Các yêu cầu kinh doanh khác nhau liên quan đến cùng một bên thứ ba khiến nhà phát triển khó chuẩn hoá việc sử dụng bên thứ ba đó.
Aurora tập trung vào tập lệnh của bên thứ ba
Một phần trong hoạt động cộng tác của Aurora với các khung và công cụ web nguồn mở là cung cấp các giá trị mặc định mạnh mẽ và công cụ có chủ đích để giúp nhà phát triển cải thiện các khía cạnh của trải nghiệm người dùng như hiệu suất, khả năng hỗ trợ tiếp cận, bảo mật và khả năng sẵn sàng cho thiết bị di động. Vào năm 2021, chúng tôi tập trung vào việc giúp các ngăn xếp khung cải thiện trải nghiệm người dùng và các chỉ số Core Web Vitals.
Một trong những bước quan trọng nhất để đạt được mục tiêu cải thiện hiệu suất khung là nghiên cứu trình tự tải lý tưởng của tập lệnh bên thứ ba trong Next.js. Các khung như Next.js có vị trí riêng biệt để cung cấp các giá trị mặc định và tính năng hữu ích giúp nhà phát triển tải tài nguyên một cách hiệu quả, bao gồm cả bên thứ ba. Chúng tôi đã nghiên cứu Kho lưu trữ HTTP và dữ liệu Lighthouse để tìm ra bên thứ ba nào chặn hiển thị nhiều nhất trên các khung.
Để giải quyết vấn đề về các tập lệnh của bên thứ ba chặn luồng chính được dùng trong một ứng dụng, chúng tôi đã xây dựng Thành phần tập lệnh. Thành phần này đóng gói các tính năng sắp xếp trình tự để cung cấp cho nhà phát triển các chế độ kiểm soát tốt hơn cho việc tải tập lệnh của bên thứ ba.
Sắp xếp trình tự tập lệnh của bên thứ ba mà không có thành phần khung
Hướng dẫn hiện có để giảm tác động của tập lệnh chặn hiển thị cung cấp các phương thức sau để tải và sắp xếp trình tự tập lệnh của bên thứ ba một cách hiệu quả:
Sử dụng thuộc tính
asynchoặcdefervới thẻ<script>để yêu cầu trình duyệt tải các tập lệnh không quan trọng của bên thứ ba mà không chặn trình phân tích cú pháp tài liệu. Các tập lệnh không cần thiết cho lần tải trang đầu tiên hoặc lượt tương tác đầu tiên của người dùng có thể được coi là không quan trọng.<script src="https://example.com/script1.js" defer></script> <script src="https://example.com/script2.js" async></script>Thiết lập kết nối sớm với các nguồn gốc bắt buộc bằng cách sử dụng kết nối trước và tìm nạp trước DNS. Điều này cho phép các tập lệnh quan trọng bắt đầu tải xuống sớm hơn.
<head> <link rel="preconnect" href="http://PreconnThis.com"> <link rel="dns-prefetch" href="http://PrefetchThis.com"> </head>Tải từng phần tài nguyên và nội dung nhúng của bên thứ ba sau khi nội dung chính của trang tải xong hoặc khi người dùng di chuyển xuống phần trang có chứa các tài nguyên và nội dung nhúng đó.
Thành phần tập lệnh Next.js
Thành phần tập lệnh Next.js triển khai các phương thức nêu trên để sắp xếp trình tự tập lệnh và cung cấp mẫu để nhà phát triển xác định chiến lược tải của họ. Sau khi bạn chỉ định chiến lược phù hợp, chiến lược đó sẽ tải một cách tối ưu mà không chặn các tài nguyên quan trọng khác.
Thành phần Script được xây dựng dựa trên thẻ <script> HTML và cung cấp một tuỳ chọn để đặt mức độ ưu tiên tải cho tập lệnh của bên thứ ba bằng thuộc tính strategy.
// Example for beforeInteractive:
<Script src="https://cdnjs.cloudflare.com/polyfill/v3/polyfill.min.js?features=IntersectionObserverEntry%2CIntersectionObserver" strategy="beforeInteractive" />
// Example for afterInteractive (default):
<Script src="https://example.com/samplescript.js" />
// Example for lazyonload:
<Script src="https://connect.facebook.net/en_US/sdk.js" strategy="lazyOnload" />
Thuộc tính strategy có thể nhận 3 giá trị.
beforeInteractive: Bạn có thể sử dụng tuỳ chọn này cho các tập lệnh quan trọng cần thực thi trước khi trang có thể tương tác. Next.js đảm bảo rằng các tập lệnh như vậy được chèn vào HTML ban đầu trên máy chủ và được thực thi trước JavaScript tự đóng gói khác. Quản lý sự đồng ý, tập lệnh phát hiện bot hoặc thư viện trợ giúp cần thiết để hiển thị nội dung quan trọng là những lựa chọn phù hợp cho chiến lược này.afterInteractive: Đây là chiến lược mặc định được áp dụng và tương đương với việc tải tập lệnh có thuộc tính trì hoãn. Bạn nên sử dụng thuộc tính này cho các tập lệnh mà trình duyệt có thể chạy sau khi trang có tính tương tác, chẳng hạn như tập lệnh phân tích. Next.js chèn các tập lệnh này ở phía máy khách và các tập lệnh này chạy sau khi trang được cấp nước. Do đó, trừ phi có quy định khác, tất cả tập lệnh của bên thứ ba được xác định bằng thành phần Tập lệnh đều được Next.js trì hoãn, nhờ đó cung cấp một giá trị mặc định mạnh mẽ.lazyOnload: Bạn có thể dùng lựa chọn này để tải chậm các tập lệnh có mức độ ưu tiên thấp khi trình duyệt ở trạng thái rảnh. Chức năng do các tập lệnh đó cung cấp không cần thiết ngay sau khi trang có thể tương tác, ví dụ: trình bổ trợ trò chuyện hoặc mạng xã hội.
Nhà phát triển có thể cho Next.js biết cách ứng dụng của họ sử dụng tập lệnh bằng cách chỉ định chiến lược. Điều này cho phép khung áp dụng các phương pháp tối ưu hoá và phương pháp hay nhất để tải tập lệnh, đồng thời đảm bảo trình tự tải tốt nhất.
Khi sử dụng thành phần Script, nhà phát triển có thể đặt tập lệnh của bên thứ ba ở bất kỳ đâu trong ứng dụng cho các bên thứ ba tải muộn và ở cấp tài liệu cho các tập lệnh quan trọng. Điều này có nghĩa là thành phần Script có thể được đặt cùng vị trí với thành phần sử dụng tập lệnh. Sau khi hydrat hoá, tập lệnh sẽ được chèn vào phần đầu của tài liệu được kết xuất ban đầu hoặc ở cuối phần nội dung, tuỳ thuộc vào chiến lược được sử dụng.
Đo lường tác động
Chúng tôi đã sử dụng các mẫu cho ứng dụng thương mại Next.js và blog khởi đầu để tạo hai ứng dụng minh hoạ giúp đo lường tác động của việc thêm tập lệnh của bên thứ ba. Ban đầu, các bên thứ ba thường dùng cho Trình quản lý thẻ của Google và nội dung nhúng trên mạng xã hội được đưa trực tiếp vào các trang của những ứng dụng này, sau đó được đưa vào thông qua thành phần Tập lệnh. Sau đó, chúng tôi so sánh hiệu suất của các trang này trên WebPageTest.
Tập lệnh của bên thứ ba trong ứng dụng thương mại Next.js
Các tập lệnh của bên thứ ba đã được thêm vào mẫu ứng dụng thương mại cho bản minh hoạ như dưới đây.
Bản so sánh sau đây cho thấy tiến trình trực quan của cả hai phiên bản bộ khởi động thương mại Next.js. Như bạn thấy, LCP xảy ra sớm hơn gần 1 giây khi thành phần Script được bật bằng chiến lược tải phù hợp.
Tập lệnh của bên thứ ba trong blog Next.js
Các tập lệnh của bên thứ ba đã được thêm vào ứng dụng blog minh hoạ như dưới đây.
| Trước | Sau |
|---|---|
| Trình quản lý thẻ của Google có tính năng không đồng bộ | Thành phần tập lệnh có strategy = lazyonload cho mỗi tập lệnh trong số 4 tập lệnh |
| Nút Theo dõi trên Twitter có tính năng không đồng bộ | |
| Nút Đăng ký YouTube không có async hoặc defer | |
| Nút Theo dõi của LinkedIn không có thuộc tính async hoặc defer |
Như trong video, Nội dung đầu tiên hiển thị (FCP) xảy ra ở giây thứ 0,9 trên trang không có thành phần Tập lệnh và giây thứ 0,4 khi có thành phần Tập lệnh.
Bước tiếp theo cho thành phần Tập lệnh
Mặc dù các lựa chọn chiến lược cho afterInteractive và lazyOnload cung cấp quyền kiểm soát đáng kể đối với các tập lệnh chặn hiển thị, nhưng chúng tôi cũng đang khám phá các lựa chọn khác để tăng tiện ích của thành phần Tập lệnh.
Sử dụng trình chạy web
Bạn có thể sử dụng Web worker để chạy các tập lệnh độc lập trên luồng nền, giúp giải phóng luồng chính để xử lý các tác vụ giao diện người dùng và cải thiện hiệu suất. Web Worker phù hợp nhất để giảm tải quá trình xử lý JavaScript, thay vì công việc giao diện người dùng, khỏi luồng chính. Các tập lệnh dùng để hỗ trợ khách hàng hoặc tiếp thị (thường không tương tác với giao diện người dùng) có thể là lựa chọn phù hợp để thực thi trên luồng nền. Bạn có thể sử dụng một thư viện bên thứ ba gọn nhẹ (PartyTown) để tách riêng các tập lệnh đó vào một trình chạy web.
Với cách triển khai hiện tại của thành phần tập lệnh Next.js, bạn nên trì hoãn các tập lệnh này trên luồng chính bằng cách đặt chiến lược thành afterInteractive hoặc lazyOnload. Trong tương lai, chúng tôi đề xuất giới thiệu một lựa chọn chiến lược mới, 'worker', cho phép Next.js sử dụng PartyTown hoặc một giải pháp tuỳ chỉnh để chạy tập lệnh trên trình chạy web. Chúng tôi hoan nghênh ý kiến đóng góp của nhà phát triển về RFC này.
Giảm thiểu CLS
Nội dung nhúng của bên thứ ba như quảng cáo, video hoặc nội dung nhúng trên mạng xã hội có thể gây ra sự thay đổi bố cục khi tải từng phần. Điều này ảnh hưởng đến trải nghiệm người dùng và chỉ số Mức thay đổi bố cục tích luỹ (CLS) của trang. Bạn có thể giảm thiểu CLS bằng cách chỉ định kích thước của vùng chứa nơi nội dung nhúng sẽ tải.
Thành phần Tập lệnh có thể được dùng để tải các nội dung nhúng có thể gây ra sự thay đổi bố cục. Chúng tôi đang cân nhắc việc bổ sung các lựa chọn cấu hình để giúp giảm CLS. Bạn có thể cung cấp tính năng này trong chính thành phần Script hoặc dưới dạng thành phần đồng hành.
Thành phần trình bao bọc
Cú pháp và chiến lược tải để đưa các tập lệnh phổ biến của bên thứ ba (chẳng hạn như Google Analytics hoặc Trình quản lý thẻ của Google (GTM)) thường được cố định. Bạn có thể đóng gói thêm các thành phần này trong các thành phần trình bao bọc riêng lẻ cho từng loại tập lệnh. Nhà phát triển chỉ có thể sử dụng một số thuộc tính tối thiểu dành riêng cho ứng dụng (chẳng hạn như mã theo dõi). Thành phần trình bao bọc sẽ giúp nhà phát triển bằng cách:
- Giúp họ dễ dàng thêm các thẻ tập lệnh phổ biến.
- Đảm bảo rằng khung sử dụng chiến lược tối ưu nhất trong nền.
Kết luận
Tập lệnh của bên thứ ba thường được tạo để thêm các tính năng cụ thể vào trang web sử dụng. Để giảm tác động của các tập lệnh không quan trọng, bạn nên trì hoãn các tập lệnh đó. Thành phần Tập lệnh Next.js sẽ thực hiện việc này theo mặc định. Nhà phát triển có thể yên tâm rằng các tập lệnh được đưa vào sẽ không làm chậm chức năng quan trọng, trừ phi họ áp dụng rõ ràng chiến lược beforeInteractive. Giống như thành phần Tập lệnh Next.js, nhà phát triển khung cũng có thể cân nhắc việc xây dựng các tính năng này trong những khung khác. Chúng tôi đang tích cực tìm hiểu cách đưa một thành phần tương tự vào Nuxt.js cùng với nhóm Nuxt.js. Dựa trên ý kiến phản hồi, chúng tôi cũng hy vọng có thể cải thiện thành phần Tập lệnh hơn nữa để đáp ứng các trường hợp sử dụng khác.
Lời cảm ơn
Cảm ơn Kara Erickson, Janicklas Ralph, Katie Hempenius, Philip Walton, Jeremy Wagner và Addy Osmani đã đưa ra ý kiến phản hồi về bài đăng này.