Tải trước mô-đun

Sérgio Gomes

Ngày xuất bản: 23 tháng 11 năm 2024

Mô hình phát triển dựa trên mô-đun mang lại một số lợi thế thực sự về khả năng lưu vào bộ nhớ đệm, giúp bạn giảm số lượng byte cần gửi đến người dùng. Độ chi tiết cao hơn của mã cũng giúp ích cho câu chuyện tải, bằng cách cho phép bạn ưu tiên mã quan trọng trong ứng dụng.

Tuy nhiên, các phần phụ thuộc của mô-đun gây ra vấn đề về việc tải, trong đó trình duyệt cần phải đợi một mô-đun tải trước khi tìm ra các phần phụ thuộc của mô-đun đó. Một cách để giải quyết vấn đề này là tải trước các phần phụ thuộc để trình duyệt biết trước về tất cả các tệp và có thể giữ kết nối bận.

<link rel="preload"> là một cách yêu cầu tài nguyên trước khi trình duyệt cần đến.

<head>
  <link rel="preload" as="style" href="critical-styles.css">
  <link rel="preload" as="font" crossorigin type="font/woff2" href="myfont.woff2">
</head>

Cách này hoạt động đặc biệt hiệu quả với các tài nguyên như phông chữ, thường được ẩn bên trong các tệp CSS, đôi khi ở nhiều cấp độ sâu. Trong trường hợp đó, trình duyệt sẽ phải đợi nhiều lượt trả về trước khi phát hiện ra rằng cần tìm nạp một tệp phông chữ lớn, trong khi thời gian đó có thể dùng để bắt đầu tải xuống và tận dụng toàn bộ băng thông kết nối.

<link rel="preload"> và tiêu đề HTTP tương đương cung cấp một cách đơn giản, khai báo để trình duyệt biết ngay về các tệp quan trọng cần thiết trong quá trình điều hướng hiện tại. Khi thấy tính năng tải trước, trình duyệt sẽ bắt đầu tải xuống tài nguyên có mức độ ưu tiên cao để khi thực sự cần, tài nguyên đó đã được tìm nạp hoặc một phần đã có. Tuy nhiên, tính năng này không hoạt động đối với các mô-đun.

Đây là phần khó khăn. Có một số chế độ thông tin xác thực cho tài nguyên và để có được một lượt truy cập vào bộ nhớ đệm, các chế độ này phải khớp với nhau, nếu không, bạn sẽ phải tìm nạp tài nguyên hai lần. Không cần phải nói, việc tìm nạp hai lần là không tốt vì làm lãng phí băng thông của người dùng và khiến họ phải chờ lâu hơn mà không có lý do chính đáng.

Đối với thẻ <script><link>, bạn có thể đặt chế độ thông tin xác thực bằng thuộc tính crossorigin. Tuy nhiên, hoá ra <script type="module"> không có thuộc tính crossorigin cho biết chế độ thông tin xác thực là omit, không tồn tại đối với <link rel="preload">. Điều này có nghĩa là bạn sẽ phải thay đổi thuộc tính crossorigin trong cả <script><link> thành một trong các giá trị khác và bạn có thể không dễ dàng thực hiện việc này nếu nội dung bạn đang cố gắng tải trước là một phần phụ thuộc của các mô-đun khác.

Hơn nữa, việc tìm nạp tệp chỉ là bước đầu tiên để thực sự chạy mã. Trước tiên, trình duyệt phải phân tích cú pháp và biên dịch mã đó. Tốt nhất là bạn nên thực hiện việc này trước để khi cần mô-đun, mã đã sẵn sàng chạy. Tuy nhiên, V8 (công cụ JavaScript của Chrome) phân tích cú pháp và biên dịch các mô-đun khác với JavaScript khác. <link rel="preload"> không cung cấp bất kỳ cách nào để cho biết tệp đang tải là một mô-đun, vì vậy, tất cả những gì trình duyệt có thể làm là tải tệp và đặt tệp đó vào bộ nhớ đệm. Sau khi tải tập lệnh bằng thẻ <script type="module"> (hoặc một mô-đun khác tải tập lệnh), trình duyệt sẽ phân tích cú pháp và biên dịch mã dưới dạng mô-đun JavaScript.

Tóm lại là có. Bằng cách có một loại link cụ thể để tải trước các mô-đun, chúng ta có thể viết HTML đơn giản mà không cần lo lắng về chế độ thông tin xác thực mà chúng ta đang sử dụng. Các giá trị mặc định hoạt động hiệu quả.

<head>
  <link rel="modulepreload" href="super-critical-stuff.mjs">
</head>
[...]
<script type="module" src="super-critical-stuff.mjs">

Và vì trình duyệt hiện đã biết rằng nội dung bạn đang tải trước là một mô-đun, nên trình duyệt có thể thông minh và phân tích cú pháp cũng như biên dịch mô-đun ngay khi tìm nạp xong, thay vì chờ cho đến khi mô-đun đó cố gắng chạy.

Hỗ trợ trình duyệt

  • Chrome: 66.
  • Edge: ≤79.
  • Firefox: 115.
  • Safari: 17.

Nguồn

Nhưng còn các phần phụ thuộc của mô-đun thì sao?

Trùng hợp làm sao bạn lại hỏi! Bài viết này thực sự chưa đề cập đến một vấn đề: đệ quy.

Thông số kỹ thuật <link rel="modulepreload"> thực sự cho phép tải không chỉ mô-đun được yêu cầu mà còn cả cây phần phụ thuộc của mô-đun đó. Trình duyệt không bắt buộc phải làm việc này, nhưng có thể.

Vậy giải pháp tốt nhất trên nhiều trình duyệt để tải trước một mô-đun và cây phần phụ thuộc của mô-đun đó là gì, vì bạn sẽ cần cây phần phụ thuộc đầy đủ để chạy ứng dụng?

Những trình duyệt chọn tải trước các phần phụ thuộc đệ quy phải có tính năng loại bỏ trùng lặp mạnh mẽ của các mô-đun. Vì vậy, nói chung, phương pháp hay nhất là khai báo mô-đun và danh sách phẳng của các phần phụ thuộc và tin tưởng trình duyệt sẽ không tìm nạp cùng một mô-đun hai lần.

<head>
  <!-- dog.js imports dog-head.js, which in turn imports
       dog-head-mouth.js, which imports dog-head-mouth-tongue.js. -->
  <link rel="modulepreload" href="dog-head-mouth-tongue.mjs">
  <link rel="modulepreload" href="dog-head-mouth.mjs">
  <link rel="modulepreload" href="dog-head.mjs">
  <link rel="modulepreload" href="dog.mjs">
</head>

Việc tải trước các mô-đun có giúp cải thiện hiệu suất không?

Tính năng tải trước có thể giúp tối đa hoá mức sử dụng băng thông bằng cách cho trình duyệt biết những nội dung cần tìm nạp để không bị treo trong những lượt truy cập trả về dài như vậy. Nếu bạn đang thử nghiệm các mô-đun và gặp phải vấn đề về hiệu suất do cây phần phụ thuộc sâu, thì việc tạo danh sách tải trước phẳng chắc chắn sẽ giúp ích.

Tuy nhiên, hiệu suất của mô-đun vẫn đang được xử lý, vì vậy, hãy nhớ xem xét kỹ những gì đang diễn ra trong ứng dụng của bạn bằng Công cụ dành cho nhà phát triển và cân nhắc việc gộp ứng dụng của bạn thành một số phần trong thời gian chờ đợi. Tuy nhiên, vẫn còn nhiều công việc liên quan đến mô-đun đang diễn ra trong Chrome, vì vậy, chúng tôi sắp có thể cho trình tạo gói nghỉ ngơi xứng đáng!