Hogyan működik loader-ként a webpack?
Először is, mit jelent az, hogy loader? Képesek vagyunk Webpack segítségével egy ES6 kompatibilis JavaScript fájlból ES5 kompatibilis változatot generálni, vagy SASS előfeldolgozóval írt stílusokból CSS fájlt létrehozni. Ezenkívül lehet képeket betölteni a html kódba külső forrásként, vagy akár base64 kódolással. Ezekhez a műveletekhez és még sok máshoz is loadereket használunk webpackben.Ha még nem kezdtétek el használni az ES6 által nyújtott lehetőségeket, itt az ideje, ehhez a Babel JS-t fogjuk használni a következő példában. Telepítsük a szükséges NPM modulokat:
npm i babel-loader babel-core babel-preset-env --save-dev
Sikeres telepítés után a webpack.config.js-ben meg kell adni a babel-loader-t a következőképpen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
const path = require('path'); const config = { entry: "./src/index", output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js' }, module: { rules: [ { use: 'babel-loader', test: /\.js$/, exclude: /node_modules/ } ] } }; module.exports = config; |
Három babel csomagot telepítettünk fel, az egyik volt a babel-loader, a másik maga a Babel JS a babel-core csomagban és a harmadik volt, amiben definiálva van, melyik JavaScript verziót kezelje a Babel. Ehhez hozzunk létre a projektünk gyökér könyvtárában egy .babelrc fájlt, aminek a tartalma a következő legyen:
1 2 3 |
{ "presets": ["babel-preset-env"] } |
1 2 3 4 5 |
import sum from './sum'; const total = sum(10, 20); console.log(total); |
1 2 3 |
const sum = (a, b) => a + b; export default sum; |
npm run build
paranccsal a kódot, akkor azt figyelhetjük meg a bundle.js -ben, hogy az ES6-os kódunkat ES5 formára hozta. Így a fejlesztői környezetben tudjuk használni az új feature-ket, éles környezetben pedig a Webpack megoldja nekünk a kompatibilitást, vagyis a Webpack a Babel JS segítségével.
Hogyan töltsünk be formázott képeket?
Ahogy a JavaScriptekhez, ugyanúgy a stíluslapokhoz is léteznek loaderek. Lehet JavaScriptbe importálni egy css fájlt, amit a CSS loader felismer és a Style loader beilleszti style tagek közé, de akár Preprocesszort is használhatunk, hiszen létezik Sass loader, Less loader, Stylus loader is. A következő példában a JavaScript fájlunkba töltsünk be egy képet és egy css fájlt, amivel a képet formázzuk. Az image.js tartalma:
1 2 3 4 5 6 |
import './styles/image.css'; const img = document.createElement('img'); img.src = 'http://lorempixel.com/400/400'; document.body.prepend(img); |
1 2 3 4 |
img { filter: drop-shadow(5px 5px 5px rgba(0,0,0,.4)); border: 5px solid gray; } |
npm i css-loader style-loader --save-dev
Ha feltelepültek a loadereket, a webpack.config.js-t egészítsük ki a következő szabályokkal:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
rules: [ { use: 'babel-loader', test: /\.js$/, exclude: /node_modules/ }, { use: [ 'style-loader', 'css-loader' ], test: /\.css$/ } ] |
Készítsünk a gyökér könyvtárba a package.json mellé egy index.html fájlt a következő tartalommal:
1 2 3 4 5 6 7 8 9 10 |
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"/> <title>Webpack Article</title> </head> <body> <script src="./dist/bundle.js"></script> </body> </html> |
1 2 3 4 5 6 |
import sum from './sum'; import './image'; const total = sum(10, 20); console.log(total); |
npm run build
paranccsal. A konzolban a kimenet most már beszédesebb:
Látható, hogy az image.js és az image.css is feldolgozásra kerül és a honlapon megjelenik a JavaScript által betöltött formázott kép:
Használhatunk Plugineket is
Az előző fejezetben megnéztük, hogyan tudja a Webpack megoldani, hogy style tag-ek közé kerüljön a css fájlunk tartalma. Most nézzük, meg azt, hogyan lehet link tag-el a Webpack által generált bundle css-t beilleszteni a html fájlunkba. Ehhez szükségünk lesz egy pluginre, aminek a neve Extract Text Webpack Plugin, telepítsük fel a következő paranccsal:npm i extract-text-webpack-plugin --save-dev
A webpack.config.js-ben fogjuk a plugint felhasználni, ahova require-rel húzzuk be legelőször, majd a modul szabályoknál a css szabályt is módosítani kell, végül pedig meg kell adni a pluginek között a plugin példányosítását. A kód a következőképpen fog kinézni:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
const path = require('path'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const config = { entry: "./src/index", output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.js' }, module: { rules: [ { use: 'babel-loader', test: /\.js$/, exclude: /node_modules/ }, { use: ExtractTextPlugin.extract({ use: 'css-loader', fallback: 'style-loader' }), test: /\.css$/ } ] }, plugins: [ new ExtractTextPlugin('style.css') ], }; module.exports = config; |
Egy probléma van ezzel, méghozzá az, hogy a scripteket és css fájlokat nekünk kell beilleszteni a html fájlba. Van erre is egy plugin, aminek a neve HTML Webpack Plugin, telepítsük a következő paranccsal:
npm i html-webpack-plugin --save-dev
Azt szeretnénk elérni, hogy a Webpack menedzselje az index.html fájlunkat, ezért másoljuk át az src mappába és szedjük ki belőle a script és a link tageket.
A webpack.config.js-ben is szükséges hozzáadni a plugint, először require-el behúzzuk a csomagot, majd a plugin részt egészítjük ki a következőképpen:
1 2 3 4 5 6 7 8 9 10 |
const HtmlWebpackPlugin = require('html-webpack-plugin'); const config = { plugins: [ new HtmlWebpackPlugin({ template: 'src/index.html' }) ] } |
A Webpack által kezelt webalkalmazásoknál, az asset fájlok cache-lését maga a Webpack oldja meg. A művelet végtelenül egyszerű: a webpack.config.js -ben a filename tulajdonságot nevezzük át ‘bundle.js’ -ről ‘bundle.[hash].js’ -re és tegyük meg ugyanezt a ‘style.css’ -nél is, ott legyen ‘style.[hash].js’ a helyettesítő, amivel kiegészítettük arra szolgál, hogy egy hash kódot illeszt a fájlnévbe, ami egyedivé teszi és mivel a fájlnév egyedi, a Webpack gondoskodik arról is, hogy az index.html fájlunkba a megfelelő asset fájlnév kerüljön:
1 2 3 4 5 6 7 8 9 10 11 |
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"/> <title>Webpack Article</title> <link href="style.86f32c63efbcfdeee607.css" rel="stylesheet" /> </head> <body> <script src="bundle.86f32c63efbcfdeee607.js"></script> </body> </html> |
npm i clean-webpack-plugin --save-dev
1 2 3 4 5 6 7 8 |
const CleanWebpackPlugin = require('clean-webpack-plugin'); const config = { plugins: [ new CleanWebpackPlugin(['dist']) ], }; |
Kedvenc funkcióm: code splitting
Itt először is hadd magyarázzam el, miről is van szó. Képzeljük el, hogy van egy beléptető oldalunk, ha a felhasználó belépett egy dashboard (irányítópult) oldallal találkozik, ahol sok grafikon, táblázat, különböző statisztikák várják. Az biztos, hogy egy beléptetéshez jóval kevesebb JavaScript-re van szükségünk, mint egy komolyabb dashboard oldalon és most képzeljük el, hogy mi lenne, ha a belépés gombra kattintva töltődnének be azok a JavaScript-ek, amikre a dashboard oldalon van szükség. Nézzük meg egy egyszerű példán keresztül. Változtassuk meg az index.js fájlunkat a következőképpen:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import sum from './sum'; const total = sum(10, 20); console.log(total); const button = document.createElement('button'); button.innerText = 'click'; button.onclick = () => { System.import('./image').then(module => { console.log(module); module.default(); }) }; document.body.appendChild(button); |
1 2 3 4 5 6 7 8 |
import './styles/image.css'; export default () => { const img = document.createElement('img'); img.src = 'http://lorempixel.com/400/400/'; document.body.prepend(img); }; |
npm run build
segítségével és nyissuk meg az index.html fájlt böngészőben, majd a Chrome DevTools-t nyissuk elő az F12 segítségével, itt kattintsunk a Network fülre és ott is a JS re szűrjünk rá. Töltsük be újra az oldalt és kattintsunk a click gombra, a következő eredményet kell látnunk:
Látható, hogy a kattintás után betöltődik a 0.bundle js, erről szól a code splitting, a szükséges scripteket csak bizonyos események bekövetkeztekor töltjük be. Ha nagyobb méretű a JS fájl akkor illik egy loading képernyőt elhelyezni a két oldal közé.
Állítsuk be a fejlesztői környezetet
Azt mindenki jól tudja, hogy a fejlesztői környezet és az éles környezet más-más funkcionalitást igényel. Ha fejlesztünk és DEV módban vagyunk, akkor szeretjük ha olvasható a forrás és nincs tömörítve, illetve a legnagyobb segítség ha Livereload/BrowserSync segítségével a kód módosításakor újratöltődik a böngésző vagy legalább is a stíluslapok. Éles környezetben viszont ezekre nincs szükség, amire viszont van, az a minify. Így tehát két különböző config-ra van szükség, hiszen le kell kezelnünk a DEV módot és a PROD módot. Kezdjünk is hozzá.Telepítsük a fejlesztői szervert a következő paranccsal:
npm i webpack-dev-server --save-dev
Ha sikeresen feltelepült a webpack-dev-server csomag, akkor vegyük fel a webpack.config.js fájlban a devServer-hez tartozó részt a következő kód alapján:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
const config = { entry: "./src/index", output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.[hash].js' }, devServer: { contentBase: [ path.resolve(__dirname, 'src'), path.resolve(__dirname, 'src/styles') ], port: 8800, open: true }, |
A következő lépésben a package.json fájlunkat fogjuk módosítani:
1 2 3 4 |
"scripts": { "build": "webpack -p", "dev": "webpack-dev-server --hot --inline" }, |
Csináltunk egy dev scriptet is, ahol a webpack-dev-server parancsot hívjuk meg a –hot és az –inline paraméterekkel. Ezek szükségesek a Webpack egyik új fejlesztéséhez a Hot Module Replacement (HMR)-hez.
Töröljük ki a build mappa tartalmát és futtassuk az
npm run dev
parancsot. Ha mindent jól állítottunk be, akkor a következő kimenetet kapjuk és egy pár másodperc után az éppen aktív böngésző ablakban megnyílik az alkalmazásunk a localhost:8800-as címen:
Próbáljunk meg módosítani az image.css vagy bármelyik JavaScript fájlban majd mentsünk rá és láthatjuk hogy a böngésző befrissül. Ha jobban szemügyre vesszük mi is történt egy érdekes dologra lehetünk figyelmesek, a kitöröld build mappa nem jön létre. Ez azért történik, mert rájöttek, hogy fejlesztői környezetben túl erőforrás igényes a folyamatos fájl írás és olvasás, ezért a HMR -nél a bundle-k a memóriában tárolódnak, így sokkal gyorsabb választ kapunk.
Az alkalmazásunkat úgy állítsuk be, hogy a fejlesztői környezetben az
npm run dev
parancsot használjuk, míg az éles környezetnél, lehetőleg a deploy során az npm run build
parancsot futtassuk.
Összegzés
Ahogy láthattuk a Webpack nem pusztán module bundler, sokkal több mindenre képes. Összehozza a frontend fejlesztő számára mind a környezetet, mind pedig a szükséges technológiákat, ahhoz hogy a lehető leghatékonyabban tudjon programozni.Ha ismerős a BrowserSync vagy a Livereload, akkor a Webpack-nek van rá megoldása, méghozzá a Hot Module Reload. Ha használtál már Gulp-ot a CSS és JS fájlok összegyúrására, fordítására, akkor most kipróbálhatod a Webpack Loaderek hogyan oldják meg ugyanezt a problémát. Ezen kívül tudjuk menedzselni a képek betöltését, a Webpack képes a cachelésre is hatékony megoldást adni, van code splitting-ünk, amivel csak a szükséges esetben töltődik be a kódunk és így tovább.
Nekem nagyon megtetszett a használata és az egyre erősödő közösség is azt bizonyítja, hogy megéri áttérni a Webpack használatára, főleg amióta kijött a Webpack 4, ahol akár 98%-kal gyorsabb is lehet a futtatás.
Szia,
ki tudod tenni a projektet github / gitlabra? Mert összeraktam a leírtak szerint a code splitting-ig, de nekem folyton elszáll mikor elindítom a build-et.
Köszönöm előre is.
Szia!
A cikkhez konkrétan nem készült repo, egy korábbi workshop-hoz viszont igen, de az sokban hasonlít a cikkre:
https://github.com/pisty/webpack-workshop
Szia,
köszönöm.
A extract-text-webpack-plugin elavult lett 4.0-tól helyette a mini-css-extract-plugin -t kell használni.