Tải động

Lệnh import được phân tích tĩnh bởi trình thông dịch JavaScript, được thực thi trước các lệnh khác trong module (lệnh import được gọi là liên kết). Do đó, mã dưới đây sẽ gây lỗi.

// Lỗi
if (x === 2) {
  import module from './module';
}

⚠️ Lưu ý: Trình thông dịch xử lý lệnh import trong giai đoạn biên dịch, không phân tích hoặc thực thi câu lệnh if, vì vậy việc đặt lệnh import trong khối mã if không có ý nghĩa và sẽ gây lỗi cú pháp, không phải lỗi thời gian chạy. Điều này có nghĩa là lệnh importexport chỉ có thể ở đầu module và không thể trong khối mã.

Thiết kế như vậy tuy có lợi cho trình biên dịch tăng hiệu suất, nhưng cũng làm cho việc tải module trong thời gian chạy không thể thực hiện. Về mặt cú pháp, không thể thực hiện tải có điều kiện. Nếu lệnh import muốn thay thế phương thức require của Node, điều này tạo ra một rào cản. Vì require là tải module trong thời gian chạy, trong khi lệnh import không thể thực hiện chức năng tải động.

const path = './' + fileName;
 
const myModual = require(path);

Trên đây là một ví dụ về tải động, require chỉ biết tải module nào chỉ khi thực thi. Lệnh import không thể làm được điều này.

Do đó, có một đề xuất để giới thiệu hàm import() để thực hiện tải động.

import(module);

Tham số module đại diện cho vị trí của module cần tải. Hàm import() có thể chấp nhận bất kỳ tham số nào, tương tự như lệnh import(), sự khác biệt chủ yếu là hàm này cho phép tải động.

import() trả về một đối tượng Promise.

const main = document.querySelector('main');
 
import(`./section-modules/${someVariable}.js`)
  .then((module) => {
    module.loadPageInto(main);
  })
  .catch((err) => {
    main.textContent = err.message;
  });

Hàm import() có thể được sử dụng ở bất kỳ đâu, không chỉ trong module, mà còn trong các tệp không phải là module. Nó được thực thi trong thời gian chạy, có nghĩa là khi nào đến lệnh đó, module được tải. Hơn nữa, hàm import() không có mối quan hệ liên kết tĩnh với module được tải, điều này khác với lệnh import. Hàm import() tương tự như phương thức require của Node, sự khác biệt chủ yếu là hàm này tải động và phương thức require tải đồng bộ.

Các trường hợp sử dụng

Tải theo nhu cầu

import() có thể được sử dụng để tải module khi cần thiết.

button.addEventListener('click', (event) => {
  import('./dialogBox.js')
    .then((dialogBox) => {
      dialogBox.open();
    })
    .catch((error) => {
      /* Xử lý lỗi */
    });
});

Trong đoạn mã trên, phương thức import() được đặt trong hàm lắng nghe sự kiện click, chỉ khi người dùng nhấp vào nút, module đó mới được tải.

Tải theo điều kiện

import() có thể được đặt trong khối mã if, tải các module khác nhau dựa trên các điều kiện khác nhau.

if (condition) {
    import('moduleA').then(...)
} else {
    import('moduleB').then(...)
}

Trong đoạn mã trên, nếu điều kiện đúng, module A sẽ được tải, ngược lại, module B sẽ được tải.

Đường dẫn module động

import() cho phép tạo đường dẫn module theo cách động.

import(fn()).then(() => {});

Trong đoạn mã trên, module sẽ được tải dựa trên kết quả trả về của hàm fn.

Lưu ý

Gán giá trị từ module nhập bằng cú pháp phân rã đối tượng

Sau khi import() tải module thành công, module đó sẽ được coi như một đối tượng và được truyền vào như tham số của phương thức then. Do đó, bạn có thể sử dụng cú pháp phân rã đối tượng để lấy các giao diện xuất.

import('./module.js').then(({ export1, export2 }) => {
  // làm gì đó
});

Trong đoạn mã trên, export1export2 đều là các giao diện được xuất từ module.js, và bạn có thể lấy chúng bằng cú pháp phân rã đối tượng.

Nhập module mặc định

Nếu module có giao diện xuất mặc định, bạn có thể nhận nó trực tiếp thông qua tham số.

import('./module.js').then((module) => {
  console.log(module.default);
});

Nhập giao diện đặt tên

Mã trên cũng có thể sử dụng cú pháp đặt tên giao diện.

import('./module.js').then(({ default: defaultInterface }) => {
  console.log(defaultInterface);
});

Tải đồng thời nhiều module

Nếu bạn muốn tải đồng thời nhiều module, bạn có thể sử dụng cú pháp sau.

Promise.all([import('./module1.js'), import('./module2.js'), import('./module3.js')]).then(
  ([module1, module2, module3]) => {
    // làm gì đó
  }
);

Nhập module trong hàm async

import() cũng có thể được sử dụng trong hàm async.

async function main() {
  const module = await import('./module.js');
 
  const { export1, export2 } = await import('./module.js');
 
  const [module1, module2, module3] = await Promise.all([
    import('./module1.js'),
    import('./module2.js'),
    import('./module3.js'),
  ]);
}
 
main();