Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> JavaScript

Dynamic Imports (Import động) trong JavaScript

Dynamic Import - Tải module khi cần (ES2020)

import() dạng function cho phép **tải module động** (lazy loading) tại runtime, thay vì static import ở đầu file. Trả về Promise resolve thành module object.

Cú pháp

import('./module.js')
    .then(module => { /* module.default, module.namedExport */ })
    .catch(err => { /* handle error */ });

// Hoặc async/await
const module = await import('./module.js');

Ví dụ minh họa

<!DOCTYPE html>
<html lang="vi">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Dynamic Imports</title>
    <style>
        body { font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; }
        .result { font-size: 18px; font-weight: 500; color: rebeccapurple; font-family: monospace; }
        button { padding: 8px 16px; cursor: pointer; }
    </style>
</head>
<body>
    <h1>Dynamic Imports trong JavaScript</h1>
    <div class="result"></div>
    <button class="btn-demo">TẢI MODULE ĐỘNG</button>
    <h3>Nhấn nút để động import module</h3>

    <script>
        const btn = document.querySelector(".btn-demo");
        const resEle = document.querySelector(".result");

        btn.addEventListener("click", loadModule);

        async function loadModule() {
            try {
                // Dynamic import - trả về Promise
                const module = await import("./sample.js");
                
                // Truy cập default export
                resEle.textContent = module.default() + "\n";
                // Hoặc module.testImport() nếu named export
                
                // Truy cập named export
                resEle.textContent += module.tellTime();
            } catch (err) {
                resEle.textContent = "Lỗi tải module: " + err.message;
            }
        }
    </script>
</body>
</html>

sample.js (Module được import động)

// sample.js
export default function testImport() {
    return 'Module testImport đã được import động\n';
}

export function tellTime() {
    return new Date().toLocaleString('vi-VN');
}

Use Cases (Trường hợp sử dụng)

1. Code Splitting / Lazy Loading (Phân chia code)

// Chỉ tải chart library khi user click xem biểu đồ
async function showChart() {
    const { Chart } = await import('chart.js');
    new Chart(ctx, config);
}

btnChart.addEventListener('click', showChart);

2. Conditional Import (Import có điều kiện)

async function loadPolyfill() {
    if (!String.prototype.startsWith) {
        await import('core-js/stable/string/starts-with');
    }
    // Polyfill đã load, có thể dùng startsWith()
}

3. Locale-based Import (i18n)

async function loadLocale(lang) {
    const messages = await import(`./locales/${lang}.js`);
    return messages.default;
}

// loadLocale('vi').then(setMessages);

4. Dynamic Import trong Node.js (CommonJS context)

// CommonJS file không dùng import static
const loadModule = async () => {
    const { default: myFunc } = await import('./esm-module.js');
    myFunc();
};

Import.meta - Metadata của module hiện tại


// Trong module
console.log(import.meta.url); // URL của file module hiện tại
console.log(import.meta.resolve('./other.js')); // Resolve URL (Stage 3)

// Dùng để load resource tương đối
async function loadJSON(relativePath) {
    const url = new URL(relativePath, import.meta.url);
    const response = await fetch(url);
    return response.json();
}

Error Handling

try {
    const module = await import('./optional-feature.js');
    module.init();
} catch (err) {
    if (err.code === 'ERR_MODULE_NOT_FOUND') {
        console.log('Tính năng không khả dụng');
    } else {
        console.error('Lỗi tải module:', err);
    }
}

Lưu ý

  • Dynamic import **luôn trả về Promise** (async).
  • Module được tải **chỉ một lần** (cached), gọi import() lại trả về cached module.
  • Cần server hỗ trợ ES Modules (HTTP, không phải file://).
  • Webpack/Rollup/Vite tự động code-split tại các điểm import().