Moduly v JavaScripte
Časť 3: UMD a natívne moduly
Milan Herda, 01/2017, úpravy 09/2022
UMD
Problém
Som vývojár populárnej javaScriptovej knižnice
Problém
Som vývojár populárnej javaScriptovej knižnice
a natívne moduly ešte nie sú "in"
Moji používatelia pracujú
- niektorí v globálnom namespace
- niektorí používajú CJS moduly
- ďalší používajú AMD moduly
Akým spôsobom budem poskytovať knižnicu?
- je lepšie ju mať ako globálnu premennú
- AMD modul?
- CJS modul?
- tri samostatné verzie a nech si používateľ stiahne tú svoju?
Odpoveď
Všetky verzie v jednom súbore
UMD
Universal Module Definition
UMD
Modul bez závislostí
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define([], factory);
} else if (typeof module === 'object' && module.exports) {
module.exports = factory();
} else {
root.ourModuleName = factory();
}
}(typeof self !== 'undefined' ? self : this, function () {
// ...
return { /* ... */ };
}));
UMD
Modul so závislosťou na knižnici foo
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(['foo'], factory);
} else if (typeof module === 'object' && module.exports) {
module.exports = factory(require('foo'));
} else {
root.ourModuleName = factory(root.foo);
}
}(typeof self !== 'undefined' ? self : this, function (foo) {
// ...
return { /* ... */ };
}));
Natívne moduly
ESM
Natívne moduly
- prišli v roku 2015 so špecifikáciou JavaScriptu ES2015 (aka ES6)
- preto skratka ESM (EcmaScript Modules)
- trvalo roky, kým sa stali automaticky podporované v rôznych prostrediach (stále nie sme v ideálnom stave)
Natívne moduly
import
- Príkaz, pomocou ktorého importujeme veci z iného modulu
-
Môže sa použiť iba v top-level úrovni (tj. nesmie byť v blokoch) a musí predchádzať iným príkazom
import abc from './utils/module1';
import { foo, bar } from './utils/module2';
foo(abc);
bar();
Natívne moduly
export
- týmto príkazom určujeme, ktoré časti modulu budú dostupné pre vonkajší svet
-
vieme ho použiť viackrát v jednom module
const abc = 'abc';
export abc;
export const bar = 'bar';
const foo = 'foo';
export default foo;
Natívne moduly
Vytvorenie projektu
yarn create vite js-modules-3 --template=vanilla
# alebo
npm create vite js-modules-3 --template=vanilla
Natívne moduly
Spustenie projektu
yarn dev
# alebo
npx run dev
Úloha
- vytvorte súbor src/theme/colors.js
- súbor bude obsahovať konštanty definujúce farby pre našu aplikáciu. Farby sú PRIMARY, SECONDARY, ERROR, WARNING, INFO
- každú farbu exportujte samostatne
- zmažte doterajší obsah main.js
- v main.js naimportuje primárnu farbu a použite ju ako farbu pozadia pre nový div element
Riešenie
src/theme/colors.js
export const PRIMARY = '#000080';
export const SECONDARY = '#008000';
export const WARNING = 'orange';
export const ERROR = '#800000';
export const INFO = '#2266cc';
Riešenie
main.js
import { PRIMARY } from './src/theme/colors.js';
const colorInfo = (name, color) => {
const divStyle = `
width: 300px;
height: 100px;
background-color: ${color};
`;
const titleStyle = `
background-color: rgba(255, 255, 255, 0.5);
`;
return `
<div style="${divStyle}">
<h3 style="${titleStyle}">${name}</h3>
</div>
`;
};
document.querySelector('#app').innerHTML = colorInfo('PRIMARY', PRIMARY);
Export
Named exports
Pomenované exporty
Named exports
- každá vec sa z modulu exportuje pod nejakým svojím menom
- pod týmto menom je potom možný aj jej import
// colors.js
export const PRIMARY = '#0000cc'
// main.js
import { PRIMARY } from './src/theme/colors.js';
Pomenované exporty
Majú viacero "foriem"
export const PRIMARY = '#0000cc';
export const SECONDARY = '#00cc00';
export const WARNING = 'orange';
export const ERROR = '#cc0000';
export const INFO = '#2266cc';
Export spojený priamo s deklaráciou
const PRIMARY = '#0000cc';
const SECONDARY = '#00cc00';
const WARNING = 'orange';
const ERROR = '#cc0000';
const INFO = '#2266cc';
export {
PRIMARY,
SECONDARY,
WARNING,
ERROR,
INFO,
};
Export predtým nadeklarovaných mien
Tzv. export zoznamu
const PRIMARY = '#0000cc';
const SECONDARY = '#00cc00';
const WARNING = 'orange';
const ERROR = '#cc0000';
const INFO = '#2266cc';
export {
PRIMARY,
SECONDARY,
};
export {
WARNING,
ERROR,
INFO,
};
Môžeme exportovať viac zoznamov z jedného modulu
const PRIMARY = '#0000cc';
const SECONDARY = '#00cc00';
const WARNING = 'orange';
const ERROR = '#cc0000';
const INFO = '#2266cc';
export {
PRIMARY as mainColor,
SECONDARY,
};
export {
WARNING as itsFine,
ERROR as itEscalatedQuickly,
INFO,
};
V zozname môžeme robiť premenovávanie
Default export
Default export
- z každého modulu môže byť jedna vec určená ako tzv. defaultný export
-
importujúci modul si ju vloží pod takým menom, akým chce
-
pri importe sa nepoužívajú { }
const PRIMARY = '#0000cc';
// export const PRIMARY = '#0000cc';
export default PRIMARY;
import mainColor from './src/theme/colors.js';
Default export
- keďže exportovaná vec nemusí mať názov, tak ňou môže byť hocijaký výraz
export default 1 + 1;
import result from './subor.js';
Default export
- export zoznamu vieme "zneužiť" na urobenie default exportu
export {
foo,
bar as default,
baz,
};
import result from './subor.js';
Import
Import pomenových exportov
Import pomenovaných exportov
- importované veci sa uvádzajú vo vnútri { }
- vymenujeme len tie, ktoré potrebujeme
import { PRIMARY, SECONDARY, ERROR } from './src/theme/colors.js';
Import pomenovaných exportov
- ak modul potrebuje, môže počas importu urobiť premenovanie
import {
PRIMARY as mainColor,
SECONDARY,
ERROR
} from './src/theme/colors.js';
Import pomenovaných exportov
- pomocou * vieme naimportovať všetko z modulu do jedného objektu
- naimportuje sa aj prípadny default export pod kľúčom default
import * as colors from './src/theme/colors.js';
console.log(colors.PRIMARY);
console.log(colors.default);
Import default exportu
Import default exportu
- default export vieme naimportovať mimo { }
- názov premennej, do ktorej sa default export vloží, je zálezitosťou importujúceho modulu
import bar from './foo.js';
Import default exportu
- v jednom import príkaze vieme kombinovať default export aj pomenovaný
import mainColor, { PRIMARY, SECONDARY } from './foo.js';
Import default exportu
- default export vieme naimportovať aj použitím kľúča default v zozname pomenovaných importov
- musíme ho ale premenovať
import { PRIMARY, default as mainColor } from './foo.js';
Import pre potreby exportu
Import pre potreby exportu
export * from "./subor.js";
export * as foo from "./subor";
export { PRIMARY, SECONDARY } from "./src/theme/colors.js";
export {
PRIMARY as primaryColor,
SECONDARY as secondaryColor
} from "./src/theme/colors.js";
export { default, PRIMARY, SECONDARY } from "./src/theme/colors.js";
Občas potrebujeme naimportovať veci z viacerých modulov len preto, aby sme ich ďalej exportovali.
- napr. v agregovaných súboroch pre viacero modulov (redux, zložené komponenty...)
Import pre potreby exportu
export {
default,
PRIMARY,
SECONDARY as secondaryColor,
} from "./src/theme/colors.js";
Jediný spôsob, ako reexportovať jedným príkazom default aj pomenový export, je pomocou zoznamu
Ak default nebude premenovaný, stáva sa default exportom pre aktuálny modul
Na čo si dať pozor
- importovaný identifikátor má živé prepojenie so svojím modulom
- do importovaného identifikátora nevieme priradiť novú hodnotu:
- pre klientský kód sa správa rovnako ako const premenná
- pre zdrojový modul však ako let
Zhrnutie
Moduly robíme, pretože
- JS kódu je veľa
- potrebujeme časti s jasnou zodpovednosťou
- modul je samostatný a tak znovupoužiteľný
- nechceme prasiť globálny namespace a zvyšovať tak riziko konfliktu názvov
Naučili sme sa:
- platnosť premenných v JS
- čo je to closure
- čo je to IIFE
- ako vyzerá module pattern
- 3 rôzne implementácie modulov:
- CommonJS
- AMD
- UMD
- ESM
- že technológia nemusí byť najlepšia, aby sa presadila. Stačí, že je dosť dobrá a k dispozícii
Vyskúšali sme si:
- npm a yarn
- webpack
- RequireJS a r.js
- vite
Videli sme:
- príklady, ako používať rôzne modulové systémy na webe a serveri
- ako by mohla vyzerať základná adresárová štruktúra JS projektu
- ako sa moduly riešili kedysi a dnes
Ďakujem za pozornosť
Moduly v JavaScripte, časť 3
By Milan Herda
Moduly v JavaScripte, časť 3
- 337