Časť 2: CommonJS a AMD
Milan Herda, 10/2016, úpravy 08/2022
CommonJS nie je súčasťou špecifikácie JavaScriptu a tak nefunguje natívne v prehliadačoch!
Všetko vo vnútri modulu je lokálne iba pre modul.
Von z modulu sa dostane iba to, čo explicitne povolíme.
require
module.exports
Premenná dostupná v každom module. Jej obsah je jediná vec, ktorá sa z modulu exportuje.
Funkcia, pomocou ktorej importujeme iné moduly.
const foo = require('./foo.js');
const bar = () => {
// ...
};
module.exports = bar;
git clone https://bitbucket.org/perungrad/js-modules-2.git
Na najvyššej úrovni máme adresáre pre štyri scenáre, ktoré dnes budeme riešiť:
V každom z týchto adresárov je ešte adresár src a v ňom sú uložené funkcie unitFactory a ranged z predchádzajúceho školenia
const unitFactory = function() {
// ...
};
module.exports = unitFactory;
src/unit/unitFactory.js
const extendByRange = function(unit) {
// ...
};
module.exports = extendByRange;
src/unit/ranged.js
const unitFactory = require('./src/unit/unitFactory.js');
const extendByRange = require('./src/unit/ranged.js');
const pikeman = unitFactory()
.setName('Pikeman')
.setSpeed(2)
.setStrength(3)
.setHealth(4);
const horseman = /* ... */
const archer = extendByRange(unitFactory())
.setName('Archer')
.setSpeed(2)
.setStrength(2)
.setHealth(4)
.setRange(4);
console.log({ pikeman });
console.log({ horseman });
console.log({ archer });
index.js
Toto bolo na serveri.
const unitFactory = function(unit) {
// ...
};
src/unit/unitFactory.js
const extendByRange = function(unit) {
// ...
};
src/unit/ranged.js
<script src="src/unit/unitFactory.js"></script>
<script src="src/unit/ranged.js"></script>
<script>
const pikeman = unitFactory();
const horseman = unitFactory();
const archer = extendByRange(unitFactory());
archer.setName('Robin Hood').setSpeed(1).setStrength(5).setHealth(10).setRange(5);
console.log({ pikeman });
console.log({ horseman });
console.log({ archer });
</script>
index.html
1. kód "modulov" je v globálnom mennom priestore
2. načítavanie cez script tag blokuje rendering stránky
3. vo väčšom projekte priveľa súborov na načítanie
Môžeme skúsiť obaliť hlavný skript a načítavanie modulov do funkcie (Module Pattern)
Môžeme napr. presunúť script tag na koniec stránky
Spojíme všetky súbory do jedného
CommonJS nie je súčasťou JS, takže nie je prítomné v prehliadačoch.
Potrebujeme knižnicu, nástroj, čokoľvek, čo nám ho dodá.
webpack je "module bundler" pre web, ktorý vie pracovať aj s CommoJS formátom
Funguje tak, že si prečíta vaše zdrojové súbory a pretransformuje ich do cieľovej formy vhodnej pre prehliadače.
Tieto pretransformované súbory sa potom nasadzujú do produkcie.
Inštalácia
cd web-commonjs
npm init
npm install --dev --save webpack webpack-cli
# alebo
yarn init
yarn add -D webpack webpack-cli
Adresárová štruktúra projektu
Konfigurácia, webpack.config.js
const path = require('path');
module.exports = {
entry: {
app: './src/index.js',
},
output: {
path: path.resolve(__dirname, 'www/js'),
filename: '[name].js',
},
mode: 'production',
};
src/index.js
const unitFactory = require('./unit/unitFactory.js');
const extendByRange = require('./unit/ranged.js');
const pikeman = unitFactory()
.setName('Pikeman')
.setSpeed(2)
.setStrength(3)
.setHealth(4);
const horseman = /* ... */
const archer = extendByRange(unitFactory())
.setName('Archer')
.setSpeed(2)
.setStrength(2)
.setHealth(4)
.setRange(4);
console.log({ pikeman });
console.log({ horseman });
console.log({ archer });
src/unit/unitFactory.js
const unitFactory = function() {
// ...
};
module.exports = unitFactory;
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>CommonJS module example</title>
<script src="www/js/app.js"></script>
</head>
<body></body>
</html>
Spustenie
npx run webpack
# alebo
yarn webpack
Pozrieme sa na vygenerovaný súbor
a objavíme
Module Pattern a IIFE!
1. kód "modulov" je v globálnom mennom priestore
2. načítavanie cez script tag blokuje rendering stránky
3. vo väčšom projekte priveľa súborov na načítanie
Môžeme skúsiť obaliť hlavný skript a načítavanie modulov do funkcie (Module Pattern)
Môžeme skúsiť moduly načítavať asynchrónne
Spojíme všetky súbory do jedného
require
require(
['path/to/module1', 'path/to/module2'],
function (module1, module2) {
// kód spustený po načítaní modulov
}
);
define
define(
'nazov', // názov je voliteľný
['path/to/module1', 'path/to/module2'], // závislosti
function (module1, module2) { // kód modulu, povinné
// kód spustený po načítaní modulov
}
);
Názov modulu je nepovinný, pokiaľ je modul v samostatnom súbore
Závislosti sú nepovinné a pokiaľ nie sú, tak sa nemusí uviesť ani prázdne pole
Závislosť môžeme uviesť ako cestu k súboru s modulom alebo ako názov modulu
AMD nie je súčasťou JS, takže nie je prítomné v prehliadačoch.
Potrebujeme knižnicu, nástroj, čokoľvek, čo nám ho dodá.
RequireJS je "module loader" pre moduly napísané v AMD formáte
Je možné ho nakonfigurovať tak, aby vedel pracovať aj s nie-AMD formátom
Inštalácia
cd web-amd
npm init
npm install --dev --save requirejs
# alebo
yarn init
yarn add -D requirejs
src/index.js
require(['unit/unitFactory', 'unit/ranged'], (unitFactory, extendByRange) => {
const pikeman = unitFactory()
.setName('Pikeman')
.setSpeed(2)
.setStrength(3)
.setHealth(4);
const horseman = /* ... */
const archer = extendByRange(unitFactory())
.setName('Archer')
.setSpeed(2)
.setStrength(2)
.setHealth(4)
.setRange(4);
console.log({ pikeman });
console.log({ horseman });
console.log({ archer });
});
src/unit/unitFactory
define(() => {
const unitFactory = function() {
// ...
}
return unitFactory;
});
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>AMD module example</title>
</head>
<body>
<script
src="node_modules/requirejs/require.js"
data-main="src/index"
></script>
</body>
</html>
AMD formát nám umožňuje mať viacero modulov v jednom súbore.
Vďaka tomu vieme optimalizovať súbory pre produkciu a spájať moduly do jedného alebo viac súborov.
RequireJS poskytuje pre tento účel utilitu nazvanú r.js
Adresárová štruktúra projektu
package.json
{
"name": "web-amd",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"requirejs": "^2.3.6"
},
"scripts": {
"build": "r.js -o baseUrl=./src paths.requireLib=../node_modules/requirejs/require include=requireLib name=index out=www/js/app.js"
}
}
/*
* r.js
* -o baseUrl=./src
* paths.requireLib=../node_modules/requirejs/require
* include=requireLib
* name=index
* out=www/js/app.js
*/
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>AMD module example</title>
</head>
<body>
<script src="www/js/app.js"></script>
</body>
</html>
Historicky vyhral CommonJS