6. března 2025

Dynamické importy

Příkazy exportu a importu, které jsme uvedli v předchozích kapitolách, se nazývají „statické“. Jejich syntaxe je velmi jednoduchá a striktní.

Za prvé, žádné parametry příkazu import nemůžeme dynamicky generovat.

Cesta k modulu musí být primitivní řetězec, ne volání funkce. Tohle nebude fungovat:

import ... from vraťNázevModulu(); // Chyba, povoleno je jen from "řetězec"

Za druhé, nemůžeme importovat podmíněně nebo za běhu skriptu:

if(...) {
  import ...; // Chyba, tohle není dovoleno!
}

{
  import ...; // Chyba, nemůžeme umístit import do bloku
}

Je to proto, že záměrem příkazů import/export je poskytnout páteř struktury kódu. To je dobrá věc, protože strukturu kódu můžeme analyzovat, moduly můžeme speciálními nástroji shromažďovat a spojovat do jednoho souboru, nepoužité exporty můžeme odstraňovat („třesení stromem“). To je možné jen proto, že struktura importů a exportů je jednoduchá a pevná.

Jak ale můžeme importovat modul dynamicky, na požádání?

Výraz import()

Výraz import(modul) načte modul a vrátí příslib, který se splní do objektu modulu obsahujícího všechny jeho exporty. Může být volán z kteréhokoli místa v kódu.

Můžeme jej používat dynamicky na kterémkoli místě kódu, například:

let cestaModulu = prompt("Který modul načíst?");

import(cestaModulu)
  .then(obj => <objekt modulu>)
  .catch(chyba => <chyba při načítání, např. když takový modul neexistuje>)

Nebo můžeme použít let modul = await import(cestaModulu), jsme-li uvnitř asynchronní funkce.

Například jestliže máme následující modul řekni.js:

// 📁 řekni.js
export function ahoj() {
  alert(`Ahoj`);
}

export function nashle() {
  alert(`Nashle`);
}

…Pak dynamický import může vypadat takto:

let {ahoj, nashle} = await import('./řekni.js');

ahoj();
nashle();

Nebo jestliže řekni.js obsahuje výchozí export:

// 📁 řekni.js
export default function() {
  alert("Modul načten (výchozí export)!");
}

…Pak můžeme pro přístup k němu použít vlastnost default objektu modulu:

let obj = await import('./řekni.js');
let řekni = obj.default;
// nebo na jednom řádku: let {default: řekni} = await import('./řekni.js');

řekni();

Zde je celý příklad:

Výsledek
say.js
index.html
export function ahoj() {
  alert(`Ahoj`);
}

export function nashle() {
  alert(`Nashle`);
}

export default function() {
  alert("Modul načten (výchozí export)!");
}
<!doctype html>
<script>
  async function načti() {
    let řekni = await import('./say.js');
    řekni.ahoj(); // Ahoj!
    řekni.nashle(); // Nashle!
    řekni.default(); // Modul načten (výchozí export)!
  }
</script>
<button onclick="načti()">Klikni na mě</button>
Poznámka:

Dynamické importy fungují i v běžných skriptech, nevyžadují script type="module".

Poznámka:

Ačkoli import() vypadá jako volání funkce, je to speciální syntaxe, která prostě jen používá závorky (podobně jako super()).

Nemůžeme tedy kopírovat import do proměnné nebo s ním používat call/apply. Není to funkce.

Mapa tutoriálu

Komentáře

přečtěte si před komentováním…
  • Máte-li návrhy na zlepšení, vytvořte prosím issue na GitHubu nebo pull request místo komentáře.
  • Pokud v článku něčemu nerozumíte, napište prosím, čemu přesně a na kterém místě.
  • Pro vložení několika slov kódu použijte značku <code>, pro několik řádků je obalte značkou <pre>, pro více než 10 řádků vložte odkaz na pískoviště (plnkr, jsbin, codepen…)