Giảm tải JavaScript bằng cách phân tách mã

Hầu hết các trang web và ứng dụng đều được tạo thành từ nhiều phần khác nhau. Thay vì gửi tất cả JavaScript tạo nên ứng dụng ngay khi trang đầu tiên được tải, việc chia JavaScript thành nhiều phần sẽ giúp cải thiện hiệu suất của trang.

Lớp học lập trình này cho biết cách sử dụng tính năng phân tách mã để cải thiện hiệu suất của một ứng dụng đơn giản sắp xếp 3 số.

Cửa sổ trình duyệt hiển thị một ứng dụng mang tên Máy sắp xếp thần kỳ, trong đó có 3 trường để nhập số và một nút sắp xếp.

Đo

Như mọi khi, điều quan trọng đầu tiên là đo lường hiệu suất của một trang web trước khi tìm cách thêm bất kỳ tính năng tối ưu hoá nào.

  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. Tải lại ứng dụng.

Bảng điều khiển mạng hiển thị gói JavaScript 71,2 KB.

JavaScript trị giá 71,2 KB chỉ để sắp xếp một vài con số trong một ứng dụng đơn giản. What gives?

Trong mã nguồn (src/index.js), thư viện lodash được nhập và sử dụng trong ứng dụng này. Lodash cung cấp nhiều hàm hiệu dụng hữu ích, nhưng chỉ có một phương thức duy nhất từ gói đang được sử dụng ở đây. Lỗi thường gặp là cài đặt và nhập toàn bộ các phần phụ thuộc của bên thứ ba, trong đó chỉ có một phần nhỏ trong số đó đang được sử dụng.

Tối ưu hoá

Bạn có thể cắt bớt kích thước gói theo một số cách sau đây:

  1. Viết một phương pháp sắp xếp tuỳ chỉnh thay vì nhập thư viện của bên thứ ba
  2. Sử dụng phương thức Array.prototype.sort() được tích hợp sẵn để sắp xếp theo số
  3. Chỉ nhập phương thức sortBy từ lodash chứ không phải toàn bộ thư viện
  4. Tải mã xuống để chỉ sắp xếp khi người dùng nhấp vào nút

Lựa chọn 1 và 2 là các phương thức hoàn toàn thích hợp để giảm kích thước gói (và có thể sẽ phù hợp nhất đối với ứng dụng thực tế). Tuy nhiên, những hàm đó không được dùng trong hướng dẫn này để giảng dạy 😈.

Cả hai cách 3 và 4 đều giúp cải thiện hiệu suất của ứng dụng này. Một số phần tiếp theo của lớp học lập trình này sẽ đề cập đến các bước này. Giống như mọi hướng dẫn lập trình khác, hãy luôn cố gắng tự viết mã thay vì sao chép và dán.

Chỉ nhập những gì bạn cần

Bạn cần sửa đổi một vài tệp để chỉ nhập một phương thức duy nhất từ lodash. Để bắt đầu, hãy thay thế phần phụ thuộc này trong package.json:

"lodash": "^4.7.0",

bằng đoạn mã sau:

"lodash.sortby": "^4.7.0",

Hiện đã có trong src/index.js, hãy nhập mô-đun cụ thể sau:

import "./style.css";
import _ from "lodash";
import sortBy from "lodash.sortby";

Và cập nhật cách các giá trị được sắp xếp:

form.addEventListener("submit", e => {
  e.preventDefault();
  const values = [input1.valueAsNumber, input2.valueAsNumber, input3.valueAsNumber];
  const sortedValues = _.sortBy(values);
  const sortedValues = sortBy(values);

  results.innerHTML = `
    <h2>
      ${sortedValues}
    </h2>
  `
});

Tải lại ứng dụng, mở Công cụ cho nhà phát triển và xem lại bảng điều khiển Mạng một lần nữa.

Bảng điều khiển mạng hiển thị gói JavaScript 15,2 KB.

Đối với ứng dụng này, kích thước gói đã giảm hơn 4 lần mà không cần nhiều thao tác, nhưng vẫn còn nhiều khả năng cần cải thiện.

Tách mã

webpack là một trong những trình đóng gói mô-đun nguồn mở phổ biến nhất hiện nay. Tóm lại, tính năng này nhóm tất cả mô-đun JavaScript (cũng như các tài sản khác) tạo nên một ứng dụng web thành các tệp tĩnh mà trình duyệt có thể đọc.

Có thể chia một gói được dùng trong ứng dụng này thành hai nhóm riêng biệt:

  • Một người chịu trách nhiệm về mã tạo nên tuyến ban đầu của chúng ta
  • Phân đoạn phụ chứa mã sắp xếp của chúng ta

Khi sử dụng tính năng nhập động, phần phụ có thể được tải từng phần hoặc được tải theo yêu cầu. Trong ứng dụng này, bạn chỉ có thể tải mã tạo nên phân đoạn khi người dùng nhấn nút.

Bắt đầu bằng cách xoá dữ liệu nhập cấp cao nhất cho phương thức sắp xếp trong src/index.js:

import sortBy from "lodash.sortby";

Sau đó, nhập sự kiện này trong trình nghe sự kiện sẽ kích hoạt khi bạn nhấn nút này:

form.addEventListener("submit", e => {
  e.preventDefault();
  import('lodash.sortby')
    .then(module => module.default)
    .then(sortInput())
    .catch(err => { alert(err) });
});

Tính năng import() là một phần của đề xuất (hiện đang ở giai đoạn 3 của quy trình TC39) để bao gồm khả năng nhập động một mô-đun. Gói web đã bao gồm hỗ trợ cho việc này và tuân theo cú pháp tương tự do đề xuất đưa ra.

import() trả về một lời hứa và khi lời hứa được giải quyết, mô-đun đã chọn sẽ được cung cấp và được chia thành một phần riêng. Sau khi mô-đun được trả về, module.default được dùng để tham chiếu dữ liệu xuất mặc định do lodash cung cấp. Lời hứa được chuỗi bằng một .then khác giúp gọi phương thức sortInput để sắp xếp 3 giá trị đầu vào. Ở cuối chuỗi lời hứa,catch() được dùng để xử lý các trường hợp lời hứa bị từ chối do lỗi.

Việc cuối cùng cần làm là viết phương thức sortInput ở cuối tệp. Đây phải là một hàm trả về một hàm lấy phương thức đã nhập từ lodash.sortBy. Sau đó, hàm lồng nhau có thể sắp xếp 3 giá trị đầu vào và cập nhật DOM.

const sortInput = () => {
  return (sortBy) => {
    const values = [
      input1.valueAsNumber,
      input2.valueAsNumber,
      input3.valueAsNumber
    ];
    const sortedValues = sortBy(values);

    results.innerHTML = `
      <h2>
        ${sortedValues}
      </h2>
    `
  };
}

Theo dõi

Tải lại ứng dụng lần cuối và theo dõi kỹ bảng điều khiển Network (Mạng). Chỉ một gói nhỏ ban đầu được tải xuống ngay khi ứng dụng tải.

Bảng điều khiển mạng hiển thị gói JavaScript 2,7 KB.

Sau khi người dùng nhấn nút này để sắp xếp các số đầu vào, phần chứa mã sắp xếp sẽ được tìm nạp và thực thi.

Bảng điều khiển mạng hiển thị gói JavaScript 2,7 KB, theo sau là gói JavaScript 13,9 KB.

Lưu ý cách các con số vẫn được sắp xếp!

Kết luận

Phân tách mã và tải từng phần có thể là những kỹ thuật cực kỳ hữu ích để giảm kích thước gói ban đầu của ứng dụng, đồng thời trực tiếp dẫn đến thời gian tải trang nhanh hơn nhiều. Tuy nhiên, có một số điều quan trọng mà bạn cần cân nhắc trước khi đưa tính năng tối ưu hoá này vào ứng dụng.

Giao diện người dùng tải từng phần

Khi tải từng phần các mô-đun mã cụ thể, bạn cần cân nhắc trải nghiệm như thế nào đối với người dùng có kết nối mạng yếu. Việc tách và tải một đoạn mã rất lớn khi người dùng gửi một thao tác có thể khiến ứng dụng có vẻ như đã ngừng hoạt động. Vì vậy, hãy cân nhắc hiển thị chỉ báo tải thuộc một số cách sắp xếp.

Tải từng phần mô-đun nút của bên thứ ba

Không phải lúc nào phương pháp này cũng là phương pháp tốt nhất để tải từng phần của bên thứ ba trong ứng dụng. Điều này còn phụ thuộc vào vị trí bạn sử dụng các phần phụ thuộc đó. Thông thường, các phần phụ thuộc của bên thứ ba được chia thành một gói vendor riêng biệt có thể được lưu vào bộ nhớ đệm vì chúng không cập nhật thường xuyên. Hãy đọc thêm về cách SplitChunksPlugin có thể giúp bạn thực hiện việc này.

Tải từng phần bằng khung JavaScript

Nhiều khung và thư viện phổ biến sử dụng gói web cung cấp các thành phần trừu tượng để tải từng phần dễ dàng hơn so với sử dụng tính năng nhập động ở giữa ứng dụng.

Bạn nên hiểu cách hoạt động của tính năng nhập động, nhưng hãy luôn sử dụng phương thức mà khung/thư viện của bạn đề xuất để tải từng phần một số mô-đun cụ thể.

Tải trước và tìm nạp trước

Nếu có thể, hãy tận dụng các gợi ý của trình duyệt như <link rel="preload"> hoặc <link rel="prefetch"> để thử và tải các mô-đun quan trọng sớm hơn. Gói web hỗ trợ cả hai gợi ý thông qua việc sử dụng chú thích diệu kỳ trong câu lệnh nhập. Điều này được giải thích chi tiết hơn trong hướng dẫn Tải trước các phần quan trọng.

Tải từng phần không chỉ là mã

Hình ảnh có thể chiếm một phần quan trọng trong ứng dụng. Tải từng phần ở dưới màn hình đầu tiên hoặc bên ngoài khung nhìn của thiết bị, có thể giúp tăng tốc độ trang web. Hãy đọc thêm về nội dung này trong hướng dẫn Lazysizes.