Tách mã bằng tính năng nhập động trong Next.js

Cách tăng tốc ứng dụng Next.js bằng tính năng phân tách mã và chiến lược tải thông minh.

Bạn sẽ học được gì?

Bài đăng này giải thích các loại phân tách mã và cách sử dụng tính năng nhập động để tăng tốc độ ứng dụng Next.js của bạn.

Phân tách mã dựa trên tuyến và thành phần

Theo mặc định, Next.js chia JavaScript của bạn thành các phần riêng biệt cho mỗi tuyến. Khi người dùng tải ứng dụng của bạn, Next.js chỉ gửi mã cần thiết cho tuyến đường ban đầu. Khi người dùng di chuyển trong ứng dụng, họ sẽ tìm nạp các đoạn liên kết với các tuyến khác. Việc phân tách mã dựa trên tuyến sẽ giúp giảm thiểu số lượng tập lệnh cần được phân tích cú pháp và biên dịch cùng một lúc, giúp tăng thời gian tải trang.

Mặc dù phân tách mã dựa trên tuyến là một mặc định hiệu quả, nhưng bạn có thể tối ưu hoá thêm quá trình tải bằng cách phân tách mã ở cấp thành phần. Nếu có các thành phần lớn trong ứng dụng, bạn nên chia các thành phần đó thành các phần riêng biệt. Bằng cách đó, mọi thành phần lớn không quan trọng hoặc chỉ hiển thị trên một số tương tác nhất định của người dùng (như nhấp vào một nút) đều có thể được tải từng phần.

Next.js hỗ trợ import() động, cho phép bạn nhập các mô-đun JavaScript (bao gồm cả các thành phần React) một cách linh động và tải mỗi lượt nhập dưới dạng một phân đoạn riêng. Thao tác này cho phép bạn phân tách mã ở cấp thành phần và cho phép bạn kiểm soát việc tải tài nguyên để người dùng chỉ tải mã họ cần cho phần của trang web mà họ đang xem. Trong Next.js, theo mặc định, các thành phần này sẽ hiển thị phía máy chủ (SSR).

Tính năng nhập động trong thực tế

Bài đăng này bao gồm một số phiên bản của ứng dụng mẫu bao gồm một trang đơn giản có một nút. Khi nhấp vào nút này, bạn sẽ thấy một chú cún con đáng yêu. Khi di chuyển qua từng phiên bản ứng dụng, bạn sẽ thấy tính năng nhập động khác với nhập tĩnh và cách xử lý chúng.

Trong phiên bản đầu tiên của ứng dụng, cún con sống trong components/Puppy.js. Để cho thấy cún con trên trang, ứng dụng sẽ nhập thành phần Puppy trong index.js bằng một câu lệnh nhập tĩnh:

import Puppy from "../components/Puppy";

Để xem cách Next.js gói ứng dụng, hãy kiểm tra hoạt động theo dõi mạng trong Công cụ cho nhà phát triển:

  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 trang.

Khi bạn tải trang, tất cả mã cần thiết, bao gồm cả thành phần Puppy.js, sẽ được đóng gói trong index.js:

Thẻ Mạng công cụ cho nhà phát triển hiển thị 6 tệp JavaScript: index.js, app.js, webpack.js, main.js, 0.js và tệp dll (thư viện liên kết động).

Khi bạn nhấn nút Nhấp vào tôi, chỉ yêu cầu về ảnh JPEG cho cún con mới được thêm vào thẻ Network (Mạng):

Thẻ Mạng công cụ cho nhà phát triển sau khi nhấp vào nút, hiển thị cùng 6 tệp JavaScript và 1 hình ảnh.

Nhược điểm của phương pháp này là ngay cả khi người dùng không nhấp vào nút để xem chó con, họ phải tải thành phần Puppy vì thành phần này có trong index.js. Trong ví dụ nhỏ này, điều đó không đáng kể, nhưng trong các ứng dụng thực tế, việc chỉ tải các thành phần lớn khi cần thiết sẽ cải thiện rất nhiều.

Bây giờ, hãy xem phiên bản thứ hai của ứng dụng, trong đó tính năng nhập tĩnh được thay thế bằng tính năng nhập động. Next.js bao gồm next/dynamic, giúp bạn có thể sử dụng tính năng nhập động cho bất kỳ thành phần nào trong Next:

import Puppy from "../components/Puppy";
import dynamic from "next/dynamic";

// ...

const Puppy = dynamic(import("../components/Puppy"));

Làm theo các bước trong ví dụ đầu tiên để kiểm tra hoạt động theo dõi mạng.

Trong lần đầu bạn tải ứng dụng, chỉ index.js được tải xuống. Lần này kích thước nhỏ hơn 0,5 KB (giảm từ 37,9 KB xuống còn 37,4 KB) vì không bao gồm mã cho thành phần Puppy:

Mạng Công cụ cho nhà phát triển hiển thị cùng 6 tệp JavaScript, ngoại trừ index.js hiện nhỏ hơn 0,5 KB.

Thành phần Puppy hiện nằm trong một phân đoạn riêng biệt là 1.js, chỉ được tải khi bạn nhấn nút:

Thẻ Mạng công cụ cho nhà phát triển sau khi nhấp vào nút, cho thấy tệp 1.js bổ sung và hình ảnh được thêm vào cuối danh sách tệp.

Trong các ứng dụng thực tế, các thành phần thường lớn hơn nhiều và việc tải từng phần có thể cắt bớt tải trọng JavaScript ban đầu của bạn đi hàng trăm kilobyte.

Tính năng nhập linh động bằng chỉ báo tải tuỳ chỉnh

Khi tải từng phần tài nguyên, bạn nên cung cấp một chỉ báo tải phòng trường hợp có sự chậm trễ. Trong Next.js, bạn có thể thực hiện việc đó bằng cách cung cấp một đối số bổ sung cho hàm dynamic():

const Puppy = dynamic(() => import("../components/Puppy"), {
  loading: () => <p>Loading...</p>
});

Để xem chỉ báo tải đang hoạt động, hãy mô phỏng kết nối mạng chậm trong DevTools:

  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. Trong danh sách thả xuống của mục Điều tiết, chọn 3G nhanh.

  6. Nhấn vào nút Nhấp vào đây.

Giờ đây, khi bạn nhấp vào nút, sẽ mất một chút thời gian để tải thành phần và ứng dụng sẽ hiển thị thông báo "Đang tải..." trong thời gian chờ đợi.

Màn hình tối có văn bản

Nhập động mà không cần SSR

Nếu chỉ cần hiển thị một thành phần ở phía máy khách (ví dụ: tiện ích trò chuyện), bạn có thể làm việc đó bằng cách đặt tuỳ chọn ssr thành false:

const Puppy = dynamic(() => import("../components/Puppy"), {
  ssr: false,
});

Kết luận

Với khả năng hỗ trợ tính năng nhập động, Next.js cho phép bạn phân tách mã ở cấp thành phần, giúp giảm thiểu tải trọng JavaScript và cải thiện thời gian tải ứng dụng. Theo mặc định, tất cả các thành phần đều được kết xuất phía máy chủ và bạn có thể tắt tuỳ chọn này bất cứ khi nào cần thiết.