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

Các tập lệnh của bên thứ ba ảnh hưởng đến hiệu suất. Do đó, bạn phải kiểm tra các tập lệnh này thường xuyên và sử dụng các kỹ thuật hiệu quả để tải 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 các tài nguyên bên thứ ba. Quá trình này bao gồm các kỹ thuật sau:

  • Trì hoãn 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 những nguồn gốc bắt buộc

Ứng dụng mẫu đi kèm có một trang web đơn giản với ba 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 trang, trong đó tài nguyên của 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 khác nhau của hiệu suất ứ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 Phối lại để chỉnh sửa để có thể chỉnh sửa dự án.
  2. Để xem trước trang web, hãy nhấn vào Xem ứng dụng. Sau đó nhấn Toàn màn hình toàn màn hình.

Chạy một Lighthouse kiểm tra hiệu suất 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 đánh dấu Hiệu suất. (Bạn có thể xoá 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 mạng 3G nhanh, Giảm 4 lần CPU.
  6. Chọn hộp kiểm Clear Storage (Xoá bộ nhớ).
  7. Nhấp vào Chạy quy trình kiểm tra.

Khi chạy một bài kiểm tra trên máy của mình, kết quả chính xác có thể khác, nhưng bạn nên lưu ý rằng thời gian Hiển thị nội dung đầu tiên (FCP) là khá cao và Lighthouse đề xuất hai 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 màu xanh lục, tối ưu hoá vẫn sẽ mang lại sự cải thiện.)

Ảnh chụp màn hình quá trình kiểm tra Lighthouse cho thấy 2,4 giây FCP và hai cơ hội: Loại bỏ các 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.

Trì hoãn JavaScript của bên thứ ba

Quy trình 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 tập lệnh đến từ d3js.org:

Ảnh chụp màn hình Loại bỏ bài kiểm tra 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à thư viện JavaScript để tạo hình ảnh dữ liệu. Tệp script.js trong ứng dụng mẫu sử dụng các hàm hiệu dụng D3 để tạo biểu đồ dạng đường SVG và nối biểu đồ đó vào trang. Thứ tự 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 được tải. Đó là lý do tại sao tài liệu này đượ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, lệnh này chặn quá trình phân tích cú pháp nội dung còn lại trong tài liệu:

Ảnh chụp màn hình củaindex.html có thẻ tập lệnh được đánh dấu trong phần đầu.

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

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

  • defer đảm bảo các tập lệnh tải xuống ở chế độ nền và thực thi sau khi quá trình 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ó tính năng chặn trình phân tích cú pháp nào.

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 trong 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

Giờ đây, 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 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 thành phần này và di chuyển lên phần tử <head> của tài liệu, ngay sau phần tử <script> của D3. Giờ đây, thao tác này không còn chặn trình phân tích cú pháp nữa 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 các tài nguyên của bên thứ ba

Tất cả các tài nguyên nằm dưới màn hình đầu tiên đều là lựa chọn phù hợp cho tính năng tải từng phần.

Ứng dụng mẫu có video YouTube được nhúng trong iframe. Để xem trang này thực hiện bao nhiêu yêu cầu và yêu cầu nào đến từ iframe YouTube được nhúng:

  1. Để xem trước trang web, hãy nhấn vào Xem ứng dụng. Sau đó nhấn 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 Mạng 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 trong Công cụ cho nhà phát triển.

Bảng điều khiển Network (Mạng) cho thấy trang này đã thực hiện 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à YouTube iframe đã đư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ả yêu cầu theo miền:

  • 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ừ khi người dùng thực sự cuộn xuống để phát video, họ không thực sự cần tất cả các nội dung đó.

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

Một cách để triển khai tính năng tải từng phần là sử dụng Intersection Observer cho trình duyệt. API này sẽ thông báo cho bạn khi một phần tử truy cập hoặc thoát khỏi khung nhìn của trình duyệt.

Bước 1: Ban đầu ngăn video tải

Để tải từng phần iframe video, trước tiên, bạn phải ngăn iframe tải theo cách thông thường. Bạn có thể 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 trong các phần tử HTML chuẩn. Bạn có thể đặt tên bất kỳ cho thuộc tính dữ liệu, miễn là thuộc tính đó bắt đầu bằng "data-".

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

Bước 2: 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 việc đó diễn ra. Đây là nơi mà API Intersection Observer API bước vào. API Intersection Observer 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 ra khỏi khung nhìn.

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

  • Nhấp vào New File (Tệp mới) rồi đặt tên cho tệp đó.
  • Nhấp vào Add This File (Thêm tệp này).

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

 <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 được danh sách các đối tượng IntersectionObserverEntry và chính đối tượng IntersectionObserver. Mỗi mục 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 gian phần tử xuất hiện trong khung nhìn và các 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ử này đi vào khung nhìn.

Trong ví dụ này, targetiframe. isIntersecting bằng true khi target đi vào khung nhìn. Để xem mã này trong 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 Xem ứng dụng. Sau đó nhấn 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ử di chuyển lên và xuống. Bạn sẽ thấy giá trị của isIntersecting thay đổi 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 dùng isIntersecting làm điều kiện để chạy hàm loadElement. Hàm này nhận giá trị từ data-src của phần tử iframe và đặt làm thuộc tính src của phần tử iframe. Quá trình 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ử mục tiêu:

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 Mạng cho thấy trang này đã thực hiện 14 yêu cầu và chỉ có 260 KB. Đó là một bước cải thiện có ý nghĩa!

Bây giờ, hãy di chuyển xuống phía dưới của trang và để ý bảng điều khiển Network (Mạng). Khi truy cập vào 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 các JavaScript không quan trọng và tải từng phần các yêu cầu YouTube. Vì vậy, bây giờ đã đến lúc tối ưu hoá nội dung còn lại của bên thứ ba.

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

Quy trình kiểm tra Lighthouse mà bạn đã thực hiện ở bước đầu tiên được đề xuất trong bài viết Kết nối trước với các nguồn gốc bắt buộc. 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 với công 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ẻ 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 quy trình kiểm tra Lighthouse khác.

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