Tối ưu hoá JavaScript của bên thứ ba

Tập lệnh của bên thứ ba ảnh hưởng đến hiệu suất, vì vậy, bạn cần kiểm tra chúng thường xuyên và sử dụng các kỹ thuật hiệu quả để tải các tập lệnh đó. Lớp học lập trình này cho bạn biết cách tối ưu hoá việc tải tài nguyên của bên thứ ba. Hướng dẫn này bao gồm các kỹ thuật sau:

  • Trì hoãn việc tải tập lệnh

  • Tải từng phần các tài nguyên không quan trọng

  • Kết nối trước với các nguồn gốc bắt buộc

Ứng dụng mẫu đi kèm có một trang web đơn giản, trong đó có 3 tính năng đến từ các nguồn của bên thứ ba:

  • Nhúng video

  • Thư viện trực quan hoá dữ liệu để kết xuất biểu đồ đường

  • Tiện ích chia sẻ trên mạng xã hội

Ảnh chụp màn hình của trang có tài nguyên bên thứ ba được làm nổi bật.
Tài nguyên của bên thứ ba trong ứng dụng mẫu.

Bạn sẽ bắt đầu bằng cách đo lường hiệu suất của ứng dụng, sau đó áp dụng từng kỹ thuật để cải thiện các khía cạnh về hiệu suất của ứng dụng.

Đo lường hiệu suất

Trước tiên, hãy mở ứng dụng mẫu ở chế độ xem toàn màn hình:

  1. Nhấp vào Remix để chỉnh sửa (Remix) để chỉnh sửa dự án.
  2. Để xem trước trang web, hãy nhấn vào View App (Xem ứng dụng), sau đó nhấn vào Fullscreen toàn màn hình (Toàn màn hình).

Chạy một quy trình kiểm tra hiệu suất Lighthouse cho bạn trên trang để thiết lập hiệu suất cơ sở:

  1. Nhấn tổ hợp phím "Control+Shift+J" (hoặc "Command+Option+J" trên máy Mac) để mở Công cụ cho nhà phát triển.
  2. Nhấp vào thẻ Lighthouse.
  3. Nhấp vào Thiết bị di động.
  4. Chọn hộp kiểm Hiệu suất. (Bạn có thể bỏ chọn các hộp đánh dấu còn lại trong phần Kiểm tra.)
  5. Nhấp vào Mô phỏng 3G nhanh, Giảm tốc độ CPU 4 lần.
  6. Chọn hộp kiểm Xoá bộ nhớ.
  7. Nhấp vào Chạy kiểm tra.

Khi chạy quy trình kiểm tra trên máy của mình, kết quả chính xác có thể khác nhau, nhưng bạn sẽ thấy rằng thời gian của Thời gian hiển thị nội dung đầu tiên (FCP) khá cao và Lighthouse đề xuất 2 cơ hội để điều tra: Loại bỏ tài nguyên chặn hiển thịKết nối trước với nguồn gốc bắt buộc. (Ngay cả khi tất cả các chỉ số đều có màu xanh lục, việc tối ưu hoá sẽ vẫn mang lại sự cải thiện.)

Ảnh chụp màn hình kiểm tra Lighthouse cho thấy FCP 2,4 giây và 2 cơ hội: Loại bỏ tài nguyên chặn hiển thị và Kết nối trước với các nguồn gốc bắt buộc.

Hoãn JavaScript của bên thứ ba

Kiểm tra Loại bỏ tài nguyên chặn hiển thị xác định rằng bạn có thể tiết kiệm thời gian bằng cách trì hoãn một tập lệnh đến từ d3js.org:

Ảnh chụp màn hình loại bỏ tài nguyên chặn hiển thị, trong đó tập lệnh d3.v3.min.js được làm nổi bật.

D3.js là một thư viện JavaScript để tạo hình ảnh trực quan dữ liệu. Tệp script.js trong ứng dụng mẫu sử dụng các hàm số hiệu dụng D3 để tạo biểu đồ dạng đường SVG và thêm biểu đồ đó vào trang. Thứ tự của các thao tác ở đây rất quan trọng: script.js phải chạy sau khi tài liệu được phân tích cú pháp và thư viện D3 đã tải. Đó là lý do thư viện D3 được đưa vào ngay trước thẻ đóng </body> trong index.html.

Tuy nhiên, tập lệnh D3 có trong <head> của trang sẽ chặn quá trình phân tích cú pháp của tài liệu còn lại:

Ảnh chụp màn hình index.html với thẻ tập lệnh được làm nổi bật ở phần đầu.

Hai thuộc tính kỳ diệu có thể bỏ chặn trình phân tích cú pháp khi thêm vào thẻ tập lệnh:

  • async đảm bảo các tập lệnh sẽ tải xuống ở chế độ nền và thực thi ngay khi có cơ hội đầu tiên sau khi các tập lệnh đó được tải xuống xong.

  • defer đảm bảo các tập lệnh sẽ tải xuống ở chế độ nền và thực thi sau khi phân tích cú pháp hoàn tất.

Vì biểu đồ này không thực sự quan trọng đối với trang tổng thể và rất có thể sẽ nằm dưới màn hình đầu tiên, hãy sử dụng defer để đảm bảo không có trình phân tích cú pháp nào chặn trình phân tích cú pháp.

Bước 1: Tải tập lệnh không đồng bộ bằng thuộc tính defer

Trên dòng 17 của index.html, hãy thêm thuộc tính defer vào phần tử <script>:

<script src="https://d3js.org/d3.v3.min.js" defer></script>

Bước 2: Đảm bảo thứ tự chính xác của các thao tác

Hiện tại, D3 bị trì hoãn nên script.js sẽ chạy trước khi D3 sẵn sàng, dẫn đến lỗi.

Các tập lệnh có thuộc tính defer sẽ thực thi theo thứ tự được chỉ định. Để đảm bảo script.js được thực thi sau khi D3 sẵn sàng, hãy thêm defer vào đó rồi di chuyển lên <head> của tài liệu, ngay sau phần tử D3 <script>. Giờ đây, lệnh này không còn chặn trình phân tích cú pháp và quá trình tải xuống sẽ bắt đầu sớm hơn.

<script src="https://d3js.org/d3.v3.min.js" defer></script>
<script src="./script.js" defer></script>

Tải từng phần tài nguyên của bên thứ ba

Tất cả các tài nguyên dưới màn hình đầu tiên đều là những tài nguyên phù hợp để tải từng phần.

Ứng dụng mẫu có video trên YouTube được nhúng trong iframe. Để kiểm tra số lượng yêu cầu mà trang đưa ra và số yêu cầu đến từ iframe đã nhúng trên YouTube, hãy làm như sau:

  1. Để xem trước trang web, hãy nhấn vào View App (Xem ứng dụng), sau đó nhấn vào Fullscreen toàn màn hình (Toàn màn hình).
  2. Nhấn tổ hợp phím "Control+Shift+J" (hoặc "Command+Option+J" trên máy Mac) để mở Công cụ cho nhà phát triển.
  3. Nhấp vào thẻ Mạng.
  4. Chọn hộp kiểm Tắt bộ nhớ đệm.
  5. Chọn 3G nhanh trong trình đơn thả xuống Điều tiết.
  6. Tải lại trang.

Ảnh chụp màn hình bảng điều khiển Mạng cho nhà phát triển.

Bảng điều khiển Network (Mạng) cho thấy trang đã gửi tổng cộng 28 yêu cầu và chuyển gần 1 MB tài nguyên nén.

Để xác định các yêu cầu mà iframe YouTube đã đưa ra, hãy tìm mã video 6lfaiXM6waw trong cột Người khởi tạo. Cách nhóm tất cả các yêu cầu theo miền lại với nhau:

  • Trong bảng điều khiển Mạng, hãy nhấp chuột phải vào tiêu đề cột.

  • Trong trình đơn thả xuống, hãy chọn cột Miền.

  • Để sắp xếp các yêu cầu theo miền, hãy nhấp vào tiêu đề cột Miền.

Cách sắp xếp mới cho thấy có thêm yêu cầu đối với các miền mua qua Google. Tổng cộng, iframe YouTube thực hiện 14 yêu cầu đối với tập lệnh, biểu định kiểu, hình ảnh và phông chữ. Nhưng trừ phi người dùng thực sự cuộn xuống để phát video, họ sẽ không thực sự cần tất cả những thành phần đó.

Bằng cách đợi tải từng phần của video cho đến khi người dùng di chuyển xuống phần đó trên trang, bạn sẽ giảm được số lượng yêu cầu mà trang đưa ra ban đầu. Phương pháp này giúp tiết kiệm dữ liệu của người dùng và tăng tốc độ tải ban đầu.

Một cách để triển khai tải từng phần là sử dụng Intersection Observer (Trình quan sát giao điểm) – một API trình duyệt thông báo cho bạn khi một phần tử vào hoặc thoát khỏi khung nhìn của trình duyệt.

Bước 1: Ngăn video tải lúc ban đầu

Để tải từng phần iframe, trước tiên, bạn phải ngăn iframe tải lên theo cách thông thường. Hãy làm việc này bằng cách thay thế thuộc tính src bằng thuộc tính data-src để chỉ định URL của video:

<iframe width="560" height="315" data-src="https://www.youtube.com/embed/lS9D6w1GzGY" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

data-src là một thuộc tính dữ liệu, cho phép bạn lưu trữ thêm thông tin về các phần tử HTML tiêu chuẩn. Bạn có thể đặt tên cho một thuộc tính dữ liệu bất kỳ, miễn là thuộc tính đó bắt đầu bằng "data-".

Một iframe không có src sẽ không tải được.

Bước 2: Sử dụng Intersection Observer để tải từng phần video

Để tải video khi người dùng cuộn đến video, bạn cần biết thời điểm tải video. Đó là khi Intersection Observer API bắt đầu. Intersection Observer API cho phép bạn đăng ký hàm callback được thực thi bất cứ khi nào một phần tử bạn muốn theo dõi vào hoặc thoát khỏi khung nhìn.

Để bắt đầu, hãy tạo một tệp mới và đặt tên tệp là lazy-load.js:

  • Nhấp vào Tệp mới và đặt tên cho tệp đó.
  • Nhấp vào Thêm tệp này.

Thêm thẻ tập lệnh vào phần đầu tài liệu của bạn:

 <script src="/lazy-load.js" defer></script>

Trong lazy-load.js, hãy tạo một IntersectionObserver mới rồi truyền vào đó một hàm callback để chạy:

// create a new Intersection Observer
let observer = new IntersectionObserver(callback);

Bây giờ, hãy cung cấp cho observer một phần tử mục tiêu để xem (trong trường hợp này là iframe video) bằng cách truyền phần tử đó dưới dạng một đối số trong phương thức observe:

// the element that you want to watch
const element = document.querySelector('iframe');

// register the element with the observe method
observer.observe(element);

callback nhận danh sách các đối tượng IntersectionObserverEntry và chính đối tượng IntersectionObserver. Mỗi mục nhập chứa một phần tử target và các thuộc tính mô tả kích thước, vị trí, thời điểm mục nhập vào khung nhìn và nhiều thông tin khác. Một trong các thuộc tính của IntersectionObserverEntryisIntersecting – một giá trị boolean bằng true khi phần tử vào khung nhìn.

Trong ví dụ này, targetiframe. isIntersecting bằng true khi target vào khung nhìn. Để xem cách này hoạt động như nào trên thực tế, hãy thay thế callback bằng hàm sau:

let observer = new IntersectionObserver(callback);
let observer = new IntersectionObserver(function(entries, observer) {
    entries.forEach(entry => {
      console.log(entry.target);
      console.log(entry.isIntersecting);
    });
  });
  1. Để xem trước trang web, hãy nhấn vào View App (Xem ứng dụng), sau đó nhấn vào Fullscreen toàn màn hình (Toàn màn hình).
  2. Nhấn tổ hợp phím "Control+Shift+J" (hoặc "Command+Option+J" trên máy Mac) để mở Công cụ cho nhà phát triển.
  3. Nhấp vào thẻ Bảng điều khiển.

Hãy thử cuộn lên và xuống. Bạn sẽ thấy giá trị thay đổi của isIntersecting và phần tử mục tiêu được ghi vào bảng điều khiển.

Để tải video khi người dùng cuộn đến vị trí của video, hãy lấy isIntersecting làm điều kiện để chạy hàm loadElement. Hàm này lấy giá trị từ data-src của thành phần iframe và đặt giá trị này làm thuộc tính src của thành phần iframe. Hành động thay thế đó sẽ kích hoạt quá trình tải video. Sau khi video được tải, hãy gọi phương thức unobserve trên observer để dừng xem phần tử đích:

let observer = new IntersectionObserver(function (entries, observer) {
  entries.forEach(entry => {
    console.log(entry.target);
    console.log(entry.isIntersecting);
  });
});
    if (entry.isIntersecting) {
      // do this when the element enters the viewport
      loadElement(entry.target);
      // stop watching
      observer.unobserve(entry.target);
    }
  });
});

function loadElement(element) {
  const src = element.getAttribute('data-src');
  element.src = src;
}

Bước 3: Đánh giá lại hiệu suất

Để xem kích thước và số lượng tài nguyên thay đổi như thế nào, hãy mở bảng điều khiển Mạng công cụ cho nhà phát triển rồi tải lại trang. Bảng điều khiển Network (Mạng) cho thấy rằng trang đã đưa ra 14 yêu cầu và chỉ có 260 KB. Đó là một sự cải tiến có ý nghĩa!

Bây giờ, hãy di chuyển xuống phía dưới của trang và chú ý đến bảng điều khiển Network (Mạng). Khi truy cập video, bạn sẽ thấy trang kích hoạt thêm yêu cầu.

Kết nối trước với các nguồn gốc bắt buộc

Bạn đã trì hoãn JavaScript không quan trọng và tải từng phần các yêu cầu YouTube, vì vậy giờ là lúc tối ưu hoá nội dung còn lại của bên thứ ba.

Việc thêm thuộc tính rel=preconnect vào một đường liên kết sẽ yêu cầu trình duyệt thiết lập kết nối với một miền trước khi yêu cầu tài nguyên đó. Bạn nên sử dụng thuộc tính này trên những nguồn gốc cung cấp tài nguyên mà bạn chắc chắn rằng trang đó cần.

Quy trình kiểm tra Lighthouse mà bạn đã chạy ở bước đầu tiên được đề xuất trong phần Kết nối trước với các nguồn gốc bắt buộc rằng bạn có thể tiết kiệm khoảng 400 mili giây bằng cách thiết lập kết nối sớm với staticxx.facebook.com và youtube.com:

Kết nối trước để kiểm tra các nguồn gốc bắt buộc, trong đó miền staticxx.facebook.com được làm nổi bật.

Vì video trên YouTube hiện được tải từng phần, nên chỉ còn lại staticxx.facebook.com, nguồn của tiện ích chia sẻ trên mạng xã hội. Việc thiết lập kết nối sớm với miền này cũng đơn giản như việc thêm thẻ <link> vào <head> của tài liệu:

  <link rel="preconnect" href="https://staticxx.facebook.com">

Đánh giá lại hiệu suất

Đây là trạng thái của trang sau khi tối ưu hoá. Làm theo các bước trong phần Đo lường hiệu suất của lớp học lập trình để chạy một bài kiểm tra Lighthouse khác.

Kiểm tra Lighthouse cho thấy FCP 1 giây và điểm hiệu suất là 99.