Direktivy pro export a import majà nÄkolik syntaktických variant.
V pÅedchozÃm Älánku jsme vidÄli jednoduché použitÃ, nynà prozkoumejme dalšà pÅÃklady.
Export pÅed deklaracemi
Kteroukoli deklaraci můžeme oznaÄit jako exportovanou tÃm, že pÅed nà uvedeme export, aÅ¥ už je to promÄnná, funkce nebo tÅÃda.
NapÅÃklad zde jsou vÅ¡echny exporty platné:
// export pole
export let mÄsÃce = ['Led', 'Ãno', 'BÅe', 'Dub', 'Srp', 'ZáÅ', 'ÅÃj', 'Lis', 'Pro'];
// export konstanty
export const ROK_VSTUPU_MODULŮ_DO_STANDARDU = 2015;
// export tÅÃdy
export class Uživatel {
constructor(jméno) {
this.jméno = jméno;
}
}
ProsÃme vÅ¡imnÄte si, že export pÅed tÅÃdou nebo funkcà z nà nedÄlá funkÄnà výraz. Je to stále deklarace funkce, byÅ¥ exportovaná.
VÄtÅ¡ina stylových průvodců JavaScriptu nedoporuÄuje stÅednÃky za deklaracemi funkcà a tÅÃd.
Z tohoto důvodu nenà na konci export class a export function nutný stÅednÃk:
export function ÅekniAhoj(uživatel) {
alert(`Ahoj, ${uživatel}!`);
} // na konci nenà ;
Export mimo deklarace
Můžeme uvést export i oddÄlenÄ.
Zde nejprve deklarujeme a pak exportujeme:
// ð Åekni.js
function ÅekniAhoj(uživatel) {
alert(`Ahoj, ${uživatel}!`);
}
function ÅekniNashle(uživatel) {
alert(`Nashle, ${uživatel}!`);
}
export {ÅekniAhoj, ÅekniNashle}; // seznam exportovaných promÄnných
â¦Nebo technicky můžeme umÃstit export i nad funkce.
Import *
Obvykle uvádÃme seznam toho, co se má importovat, ve složených závorkách import {...}, napÅÃklad:
// ð main.js
import {ÅekniAhoj, ÅekniNashle} from './Åekni.js';
ÅekniAhoj('Jan'); // Ahoj, Jan!
ÅekniNashle('Jan'); // Nashle, Jan!
Pokud je toho vÅ¡ak hodnÄ, můžeme importovat vÅ¡echno jako objekt použitÃm import * as <obj>, napÅÃklad:
// ð main.js
import * as Åekni from './Åekni.js';
Åekni.ÅekniAhoj('Jan');
Åekni.ÅekniNashle('Jan');
Na prvnà pohled vypadá âimport vÅ¡ehoâ jako bezvadná vÄc, krátce se zapisuje, proÄ bychom tedy vůbec nÄkdy mÄli výslovnÄ uvádÄt seznam toho, co potÅebujeme importovat?
Je k tomu ovÅ¡em nÄkolik důvodů.
- Výslovné uvedenà toho, co se má importovat, nám umožÅuje psát kratšà názvy:
ÅekniAhoj()mÃstoÅekni.ÅekniAhoj(). - Výslovný seznam importů nám dává lepšà pÅehled o struktuÅe kódu: vidÃme, co a kde je použito. UsnadÅuje podporu a refaktorizaci kódu.
Modernà sestavovacà nástroje, napÅ. webpack a jiné, spojujà moduly dohromady a optimalizujà je, aby urychlily naÄÃtánÃ. RovnÄž odstraÅujà nepoužité importy.
Jestliže napÅÃklad importujete import * as knihovna z obrovské knihovny kódu a pak použijete jen nÄkolik metod, nepoužité metody nebudou zahrnuty do optimalizovaného svazku.
Import âasâ
Můžeme také importovat pod odlišnými názvy, a to pomocà as.
NapÅÃklad importujme ÅekniAhoj pro struÄnost do lokálnà promÄnné ahoj a importujme ÅekniNashle jako nashle:
// ð main.js
import {ÅekniAhoj as ahoj, ÅekniNashle as nashle} from './Åekni.js';
ahoj('Jan'); // Ahoj, Jan!
nashle('Jan'); // Nashle, Jan!
Export âasâ
Podobná syntaxe existuje i pro export.
Exportujme funkce pod názvy ahoj a nashle:
// ð Åekni.js
...
export {ÅekniAhoj as ahoj, ÅekniNashle as nashle};
Nynà jsou ahoj a nashle oficiálnà názvy pro vnÄjšà kód, které budou použity v importech:
// ð main.js
import * as Åekni from './Åekni.js';
Åekni.ahoj('Jan'); // Ahoj, Jan!
Åekni.nashle('Jan'); // Nashle, Jan!
Export default
V praxi se použÃvajà pÅevážnÄ dva druhy modulů.
- Moduly, které obsahujà knihovnu, balÃÄek funkcÃ, podobnÄ jako
Åekni.jsvýše. - Moduly, které deklarujà jedinou entitu, napÅ. modul
uživatel.jsexportuje pouzeclass Uživatel.
VÄtÅ¡inou se dává pÅednost druhému uvedenému pÅÃstupu, takže každá âvÄcâ sÃdlà ve svém vlastnÃm modulu.
PÅirozenÄ to vyžaduje spoustu souborů, jelikož vÅ¡echno chce svůj vlastnà modul, ale to vůbec nenà problém. Ve skuteÄnosti je navigace v kódu snadnÄjÅ¡Ã, jsou-li soubory dobÅe pojmenovány a strukturovány ve složkách.
Moduly poskytujà speciálnà syntaxi export default (âvýchozà exportâ), aby pÅÃstup âjedna vÄc v jednom moduluâ vypadal lépe.
UmÃstÄte export default pÅed entitu, která se má exportovat:
// ð uživatel.js
export default class Uživatel { // jen pÅidáme âdefaultâ
constructor(jméno) {
this.jméno = jméno;
}
}
V jednom souboru může být pouze jeden export default.
â¦A pak jej importujte bez složených závorek:
// ð main.js
import Uživatel from './uživatel.js'; // ne {Uživatel}, jen Uživatel
new Uživatel('Jan');
Importy bez složených závorek vypadajà lépe. Obvyklá chyba v zaÄátcÃch použÃvánà modulů je zapomÃnat uvádÄt složené závorky úplnÄ. Proto si pamatujte, že import potÅebuje složené závorky pro pojmenované exporty a nepotÅebuje je pro výchozÃ.
| Pojmenovaný export | Výchozà export |
|---|---|
export class Uživatel {...} |
export default class Uživatel {...} |
import {Uživatel} from ... |
import Uživatel from ... |
Technicky můžeme mÃt v jednom modulu souÄasnÄ výchozà i pojmenované exporty, ale v praxi je lidé obvykle nesmÄÅ¡ujÃ. Modul obsahuje buÄ pojmenované exporty, nebo výchozà export.
Protože v jednom souboru může být nanejvýše jeden výchozà export, exportovaná entita nemusà mÃt název.
NapÅÃklad tohle vÅ¡echno jsou zcela platné výchozà exporty:
export default class { // žádný název tÅÃdy
constructor() { ... }
}
export default function(uživatel) { // žádný název funkce
alert(`Ahoj, ${uživatel}!`);
}
// exportujeme jedinou hodnotu, aniž bychom vytvoÅili promÄnnou
export default ['Led', 'Ãno', 'BÅe', 'Dub', 'Srp', 'ZáÅ', 'ÅÃj', 'Lis', 'Pro'];
Neuvést název je v poÅádku, protože v jednom souboru je jen jeden export default, takže import bez složených závorek vÃ, co má importovat.
Bez default by takový export ohlásil chybu:
export class { // Chyba! (nevýchozà export vyžaduje název)
constructor() {}
}
âVýchozÃâ název
V nÄkterých situacÃch se klÃÄové slovo default použÃvá k odkazu na výchozà export.
NapÅÃklad k exportu funkce oddÄlenÄ od jejà definice:
function ÅekniAhoj(uživatel) {
alert(`Ahoj, ${uživatel}!`);
}
// totéž, jako bychom pÅed funkcà uvedli âexport defaultâ
export {ÅekniAhoj as default};
Nebo jiná situace: ÅeknÄme, že modul uživatel.js exportuje jednu hlavnà âvýchozÃâ vÄc a nÄkolik pojmenovaných (vzácný pÅÃpad, ale stává se to):
// ð uživatel.js
export default class Uživatel {
constructor(jméno) {
this.jméno = jméno;
}
}
export function ÅekniAhoj(uživatel) {
alert(`Ahoj, ${uživatel}!`);
}
TÃmto způsobem importujeme výchozà export spoleÄnÄ s pojmenovaným:
// ð main.js
import {default as Uživatel, ÅekniAhoj} from './uživatel.js';
new Uživatel('Jan');
A nakonec, jestliže importujeme vÅ¡echno * jako objekt, pak vlastnost default je pÅesnÄ výchozà export:
// ð main.js
import * as uživatel from './uživatel.js';
let Uživatel = uživatel.default; // výchozà export
new Uživatel('Jan');
Pár slov proti výchozÃm exportům
Pojmenované exporty jsou explicitnÃ. To, co exportujÃ, majà pÅesnÄ pojmenováno, takže od nich tuto informaci dostaneme; to je dobrá vÄc.
Pojmenované exporty nás nutà pÅi importu použÃvat pÅesnÄ ten správný název:
import {Uživatel} from './uživatel.js';
// import {MůjUživatel} nefunguje, název musà být {Uživatel}
â¦ZatÃmco u výchozÃho exportu si pÅi importu název vždy volÃme:
import Uživatel from './uživatel.js'; // funguje
import MůjUživatel from './uživatel.js'; // funguje také
// může být import Cokoli... a poÅád to bude fungovat
Älenové týmu tedy mohou použÃvat různé názvy pÅi importu stejné vÄci, a to nenà dobré.
Abychom se tomu vyhnuli a udrželi kód konzistentnÃ, obvykle platà pravidlo, že importované promÄnné by mÄly odpovÃdat názvům souborů, napÅÃklad:
import Uživatel from './uživatel.js';
import PÅihlaÅ¡ovacÃFormuláŠfrom './pÅihlaÅ¡ovacÃFormuláÅ.js';
import funkce from '/cesta/do/funkce.js';
...
I tak to ovÅ¡em nÄkteré týmy považujà za vážnou nevýhodu výchozÃch exportů. Dávajà tedy pÅednost použÃvánà výhradnÄ pojmenovaných exportů. I když je exportována pouze jediná vÄc, je stále exportována pod svým názvem, bez default.
To také trochu usnadÅuje reexport (viz dále).
Reexport
Syntaxe âreexportuâ export ... from ... nám umožÅuje importovat vÄci a okamžitÄ je exportovat (tÅeba i pod jiným názvem), napÅÃklad:
export {ÅekniAhoj} from './Åekni.js'; // reexport ÅekniAhoj
export {default as Uživatel} from './uživatel.js'; // reexport výchozÃho exportu
K Äemu by to bylo potÅeba? PodÃvejme se na praktický pÅÃpad použitÃ.
PÅedstavme si, že pÃÅ¡eme âbalÃÄekâ: složku s mnoha moduly, nÄkterá funkcionalita z nich bude exportována ven (publikovánà a distribuci takových balÃÄků nám umožÅujà nástroje jako NPM, ale nemusÃme je použÃvat) a mnoho modulů je jen âpomocnýchâ pro vnitÅnà použità v jiných modulech balÃÄku.
Struktura souborů by mohla být takováto:
auth/
index.js
uživatel.js
pomocné.js
testy/
login.js
poskytovatelé/
github.js
facebook.js
...
Rádi bychom vystavili funkcionalitu celého balÃÄku v jediném vstupnÃm bodu.
Jinými slovy, ÄlovÄk, který by chtÄl náš balÃÄek použÃvat, by mÄl importovat jen z âhlavnÃho souboruâ auth/index.js.
NapÅÃklad takto:
import {login, logout} from 'auth/index.js'
âHlavnà souborâ auth/index.js exportuje veÅ¡kerou funkcionalitu, kterou bychom v naÅ¡em balÃÄku chtÄli poskytnout.
MyÅ¡lenkou je, že lidé zvnÄjÅ¡ku, jinà programátoÅi použÃvajÃcà náš balÃÄek, by se nemÄli zabývat jeho vnitÅnà strukturou a hledat soubory uvnitÅ složky s naÅ¡Ãm balÃÄkem. V auth/index.js exportujeme jen to, co je nutné, a zbytek je pÅed zvÄdavýma oÄima ukryt.
Protože naÅ¡e skuteÄná exportovaná funkcionalita je roztrouÅ¡ena po celém balÃÄku, můžeme ji importovat do auth/index.js a exportovat z nÄj:
// ð auth/index.js
// importujeme pÅihlas/odhlas a ihned je exportujeme
import {pÅihlas, odhlas} from './pomocnÃci.js';
export {pÅihlas, odhlas};
// importujeme výchozà export jako Uživatel a exportujeme ho
import Uživatel from './uživatel.js';
export {Uživatel};
...
Nynà mohou uživatelé naÅ¡eho balÃÄku použÃt import {pÅihlas} from "auth/index.js".
Syntaxe export ... from ... je jen kratšà notace takového importu-exportu:
// ð auth/index.js
// reexport pÅihlas/odhlas
export {pÅihlas, odhlas} from './pomocnÃci.js';
// reexport výchozÃho exportu jako Uživatel
export {default as Uživatel} from './uživatel.js';
...
Významný rozdÃl export ... from ve srovnánà s import/export spoÄÃvá v tom, že reexportované moduly nejsou v aktuálnÃm souboru dostupné. UvnitÅ uvedeného pÅÃkladu auth/index.js tedy nemůžeme použÃvat reexportované funkce pÅihlas/odhlas.
Reexportovánà výchozÃho exportu
Výchozà export musà být pÅi reexportu zpracován oddÄlenÄ.
ÅeknÄme, že máme soubor uživatel.js obsahujÃcà export default class Uživatel a chtÄli bychom ji reexportovat:
// ð uživatel.js
export default class Uživatel {
// ...
}
Můžeme s tÃm narazit na dva problémy:
-
export Uživatel from './uživatel.js'nefunguje. Povede k syntaktické chybÄ.Abychom reexportovali výchozà export, musÃme napsat
export {default as Uživatel}, jako v uvedeném pÅÃkladu. -
export * from './uživatel.js'reexportuje pouze pojmenované exporty, ale ignoruje výchozÃ.Pokud chceme reexportovat jak pojmenované, tak výchozà exporty, potÅebujeme dva pÅÃkazy:
export * from './uživatel.js'; // reexport pojmenovaných exportů export {default} from './uživatel.js'; // reexport výchozÃho exportu
Tyto zvláštnosti reexportu výchozÃho exportu jsou jednÃm z důvodů, proÄ nÄkteÅà vývojáÅi nemajà rádi výchozà exporty a dávajà pÅednost pojmenovaným.
ShrnutÃ
Zde jsou vÅ¡echny druhy export, které jsme uvedli v této a v pÅedchozà kapitole.
Můžete vyzkouÅ¡et sami sebe, když si je pÅeÄtete a pokusÃte se vzpomenout si, co znamenajÃ:
- PÅed deklaracà tÅÃdy/funkce/â¦:
export [default] class/function/variable ...
- Samostatný export:
export {x [as y], ...}.
- Reexport:
export {x [as y], ...} from "modul"export * from "modul"(nereexportuje výchozà export).export {default [as y]} from "modul"(reexportuje výchozà export).
Import:
- Import pojmenovaných exportů:
import {x [as y], ...} from "modul"
- Import výchozÃho exportu:
import x from "modul"import {default as x} from "modul"
- Import všeho:
import * as obj from "modul"
- Import modulu (spustà se jeho kód), ale bez pÅiÅazenà jeho exportů do promÄnných:
import "modul"
PÅÃkazy import/export můžeme uvést na zaÄátku nebo na konci skriptu, na tom nezáležÃ.
Technicky je tedy tento kód v poÅádku:
ÅekniAhoj();
// ...
import {ÅekniAhoj} from './Åekni.js'; // import na konci souboru
V praxi se importy obvykle uvádÄjà na zaÄátku souboru, ale to je jen pro vÄtšà pÅehlednost.
ProsÃme vÅ¡imnÄte si, že pÅÃkazy import/export nefungujÃ, jsou-li uvnitÅ {...}.
PodmÃnÄný import, napÅÃklad tento, nefunguje:
if (nÄco) {
import {ÅekniAhoj} from "./Åekni.js"; // Chyba: import musà být na nejvyššà úrovni
}
â¦Co když vÅ¡ak opravdu potÅebujeme nÄco importovat podmÃnÄnÄ? Nebo ve správnou dobu? NapÅÃklad naÄÃst modul na požádánà až tehdy, když je opravdu zapotÅebÃ?
Dynamické importy uvidÃme v dalÅ¡Ãm Älánku.
KomentáÅe
<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â¦)