Webpack (osnove)

Uvod

Prednosti i mane modularnog programiranje

webpack logo

Organizacija koda samo u jednom fajlu, čini programiranje veoma komplikovanim. Iz tog razloga se u JavaScript-u kao i u mnogim drugim programskim jezicima, programski kod deli u manje celine tzv. module. Podela se vrši prema odredjenoj šemi, koja omogućava dobru organizaciju i olakšava vizuelnu i strukturnu preglednost.

Pri deljenju na manje delove (module), dešava se da jedan modul zavisi od koda koji se nalazi u drugom modulu. To se prevazilazi “uvoženjem” (eng. import) koda iz jednog modula u drugi. Sve do pojave ES2015 standarda nije postojla sintaksa za module, pa se koristila eksterna sintaksa CommonJS ili AMD. Webpack podrazumevano koristi “CommonJS” sintaksu, pa se tako u okviru webpack.config.js fajla mogu naći ključne reči require() i module.export. Medjutim ako se u projekat implementira “Babel”, preporuka je da se koristi JavaScript sintaksa ugradjena u sam jezik prema ES2015 standardu.

OBJAŠNJENJE:
Externe sintakse nisu JavaScript biblioteke, već odgovarajuće specifikacije i konvencije za definisanje modula. Eksterna sintaksa može da se koristi jedino uz pomoć modul loader-a ili bundler-a koji joj “udahnu život”. Kada modul loader/bundler podržava sintaksu, to znači da zna da primeni ugradjene metode koje su prethodno zamišljene da se koriste kroz odredjenu sintaksu. Više ovome pogledajte u članku Modularno programiranje – eksterna sintaksa (AMD & CommonJS)

JavaScript modul od kojeg zavisi rad drugog modula se zove “zavisnost” (eng. “dependency”). Podela koda na više manjih modula ima svojih prednosti medjutim pri tome se javljaju sledeći problemi koji treba da se prevaziđu:

  • povećan broja request-a za učitavanje tih fajlova ka serveru.
  • organizovanje redosleda učitavanja modula, i svih njihovi zavisnosti (eng. dependencies)

Namena bundler-a

“Modul bundler” rešava probleme nastale usitnjavanjem koda, tako što kompajlira sve module u jedan fajl, pri čemu vodi računa da moduli koji su zavisnost drugome modulu budu učitani pre modula kome trebaju.

Najpoznatiji modul bundler-i su:

  • Browserify – implementira CommonJS sintaksu u browser okruženju. Može da se nadogradjuje raznim plugin-ima. Uz pomoć task runner-a (Gulp ili Grunt) može da završi različite zadatke vezane za obradu programskog koda.

  • Webpack – učitavati module u bilo kom popularnom formatu (CommonJS, UMD, AMD, ES6). Dolazi kao jedan paket i nisu mu potrebni dodatni plugin. Oficijelna stranica za webpack je https://webpack.github.io/, dok se dokumentacija za webpack 2 nalazi na https://webpack.js.org/

    OBJAŠNJENJE:
    Glavna namena “webpack-a” je da objedini sve module u jedan ili više krupnijih fajlova, dok dodatne funkcionalnosti webpack dobija kroz “webpack loader-e” ili “webpack plugin-e”.

Instalacija webpack-a

Pošto se instalacija vrši preko package manager-a, potrebno je imati instaliran node.js i odgovarajući package manager. Nakon toga koristeći “package manager” instaliramo i sam webpack:



NAPOMENA:
Svi primeri iz članka objedinjeni su u jedan projekat. Završna verzija projekta je objavljena na GitHub-u
https://github.com/choslee/webprogramiranje-webpackOsnove.

Osnovna konfiguracija webpack-a

Konfiguraciju webpacka definišemo u okviru webpack.congfig.js fajla. U njemu se koristi common.js sintaksa, pa se primećuju ključne reči require() i module.exports = {…}. Više o common.js pogledajete u članku Modularno programiranje sa eksternom sintaksom

NAPOMENA:
Kad god se promeni webpack.config.js, potrebno je da se ponovo generišu (“build-uju”) fajlovi. Ukoliko se koristi “webpack –watch” opcija, onda je potrebno prekinuti proces sa CTRL + C i ponovo gs uključiti.

Definisanje ulaznog i generisanje izaznog fajla

Pošto smo već pri generisanju package.json fajla definisali šta je ulazna tačka (npr. “index.js”), sada tu činjenicu možemo da iskoristimo da definišemo ulaznu tačku i za webpack. Početni i izlazni fajl za webpack definišemo u okviru webpack.config.js:

Prethodna konfiguracija podrazumeva da će se fajl bundle.js generisati u root-u projekta, medjutim ukoliko želimo da se bundle.js generiše na drugom mestu potrebno je dodati svojstvu output novo svojstvo path. Vrednost svojstva path je apsolutna putanja, stoga koristimo node.js promenjivu __dirname koja predstavlja apsolutnu putanju foldera u kome se nalazi modul.

Umesto path: __dirname + "/dist" možemo da koristimo sa potpuno istim rezultatom i node.js metodu path.resolve([...paths]). Da bi koristili path.resolve() metodu u okviru webpack.config.js fajla moramo da uvezemo modul require ('path') na vrhu fajla.

Definisanje višestrukih ulaznih i generisanje više izlaznih fajlova

U dosadašnjem delu je kroz “entry” i “output” svojstva definisan samo po jedan ulazni i jedan krajnji javascript fajl, što je najčešća konfiguracija za “Single Page Application” (tzv. SPA). Medjutim ako vaša aplikacija ima više stranica velika šansa je da će biti zahteva se na svakoj učitava drugi JavaScript bundle fajl. WebPack obezbedjuje i tu funkcionalnost, samo je potrebno da se u konfiguaracionom fajlu definiše više ulaznih (entry) fajlova i da se napiše kod koji dinamički generiše više krajnjih (“outputh”) JavaScript fajlova:

Ovako definisani ulazni i izlazni fajlovi će generisati dva nova JavaScript bundle fajla: “index.bundle.js” i “druga.bundle.js”. Nazivi entry svojstva indexJS i drugaJS su “chunks” imena, koja se koriste kasnije kod HtmlWebpackPlugin plugina. Na ovaj način smo omogućili da dve različite skripte mogu biti ugradjene na dve različite stranice.

Pored prethodno opisanog definisanja višestrukih ulaza i izlaza, često se javlja potreba za razdvajenjem javascript koda na naš kod i tzv. “third party dependencies”. Ovaj postupak se malo razlikuje od opisanog i za njega je potrebno koristiti plugin koji sprečava dupliranje koda. O ovome možete pročitati u sekciji “CommonsChunkPlugin”.

Pokretanje webpack-a

Standardno pokretanje

Pokretanje webacka je isto kao pokretanje bilo kog drugog npm paketa.



Webpack watch

Da bi izbegli neprestano pozivanje naredbe npm run webpack koja build-uje nove promene, postoji mogućnost da napišemo skriptu u okviru package.json fajla koja uz flag --watch omogućava build-ovanje nakon čuvanja promena unutar projekta.

Posled definisanja skripte u package.json možemo u terminalu pokrenuti naredbu koja će pratiti promene:



Za prekidanje praćenja promena u terminalu koristimo prečicu:

Nakon čega se od nas zahteva potvrdimo prekid i upišemo Y.

Webpack dev server

Webpack obezbedjuje i jednostavni web server koji nam daje mogućnost da automatski učitatvamo promene koda u browseru tzv. “live reloading“, nakon čuvanja promena u editoru.

Instalacija web servera



Konfiguracija webpack-a za dev server

Osnovna konfiguracija se svodi na definisanje foldera koji treba da prikazuje, stoga je potrebno unutar “webpack.config.js” fajla ispod dela gde je konfigurisan “devtool” dodati deo:

Pokretanje dev servera

Za jednostavno pokretanje servera je potrebno definisati skriptu u okviru “package.json” fajla

Ovo nam omogućava da iz terminala jednostavnom naredbom podignemo server i prikažemo sve iz “dist” foldera. Flag --open nam omogućava da se taj prikaz izvrši u novom tabu u okviru browsera.



Webpack dev server će nakon svake promene JS ili CSS fajla, automatski ponovno učitati promene u browseru. Za prekidanje rada servera u terminalu koristimo prečicu:

Nakon čega se od nas zahteva potvrdimo prekid i upišemo Y.

Više o webpack dev serveru možete pogledati na WebPack oficijelnoj stranici u sekciji “Using webpack dev server”

BrowserSync & Webpack dev server

BrowserSync je takodje webserver i može isto kao WebPack dev server samostalno da automatski učitava promene u browser tzv. “Live reloading”. Pored ove mogućnosti BrowserSync omogućava istovremeno sinhronizovano testiranje na više različitih uredjaja (mobilini, tablet…) dok su na lokalnom serveru pod uslovom da se nalaze na istoj mreži. Ovo podrazumeva da se jedna ista radnja (npr. klik na dugme, skrol stranice…) istovremeno izvodi na svim aktivnim uredjajima.
Kada se pored Webpack dev servera instalira BrowserSync onda imamo aktivna dva servera, ali u tom slučaju se BrowserSync ponaša kao medijator (proxy server). Na ovaj način smo u mogućnosti da koristimo najbolje funkcije od ta dva.

Instalacija Browsersync

Browsersync app se samostalno instalira (pogledajte na oficijelnom sajtu browsersync.io/)



Kada se koristi uz webpack instalira se kao plugin.



Konfiguracija BrowserSync plugina

Potrebno je na vrhu webpack.config.js fajla importovati plugin:

A u delu zaduženom za plugine dodati:

Pored ovoga potrebno je isključiti opciju da webpack dev server otvara novi tab, jer se takodje otvara i preko BrowserSynca-a pa bi došlo do dupliranja. Zbog toga je neophodno da obrišemo --open na mestu gde smo prethodno definisali script u okviru package.json fajla.

Startovanje BrowserSync

Browsersync url

BrowserSync se sam startuje kada je Webpack u “watch” modu! Nakon startovanja BrowserSync će na kraju ispisa u terminalu dostaviti url adrese za sinhronizovano pristupanje projektu sa dva ili više uredjaja. Dovoljno je da se u vaš mobilni unese obeležena adresa i uredjaji će biti povezani na vašem projektu. Sve što se radi na jednom uredjaju paralelno se izvršava i na drugom. Više o ovome možete pročitati na oficijlnom sajtu “BrowserSync for webpack”.

Webpack loaders

Namena

Glavna namena Webpacka je da bude bundler tj. da okupi sve JavaScript fajlove u jedan veliki fajl prema odgovarajućem redosledu koji uvažava njihove zavisnosti. Medjutim pre pomenutih radnji mogu da se izrvše odredjene “predradnje” nad tim fajlovima, a za to su zaduženi webpack loaders. Loaderi preprocesuiraju fajlove pre nego što ih sam webpack obradi. Loader mogu:

  • da integrišu CSS fajlove u JavaScript
  • da transformišu fajlove sa drugog jezika u JavaScript (transpiliraju), npr. Typescript u JavaScript ili ES6+ u JavaScript.
  • da minifikuju fajlove (CSS/JavaScript) u JavaScript
  • da transformišu inline slike u base64-encoded URL string koji tako mogu da budu dostupni JavaScript-u, nakon čega on može da ih ubacuje na stranu.
  • da učitavaju i kompajliraju cele framework-e (npr.Vue.js, Angular.js…)
  • da testiraju kod (npr. Mocha…)
  • da lintuju kod (npr. ESlint…)
  • da učitaju i kompajliraju templating sistem u HTML (npr. Handlebar…)

Loaderi mogu da se nadovezuju u serijama (chain together). Listu postojećih loader-a možete pogledati na oficijelnom sajtu webpack.js.org/loaders/

CSS loader

Instalacija paketa za CSS

Ako javascript radi obradu CSS-a (minifikovanje, transpiliranje…) potrebno je uvesti taj CSS fajl u javascript modul koristeći sintaksu import '../css/main.css'. Da bi javascript prepoznao ovu sintaksu potrebno je koristiti “css-loader”. CSS loader “hendluje” importovanje CSS koda tako što ga generiše kao string.
Ukoliko pored toga želimo i da ugradimo CSS u sam Javascript modul, onda koristimo “style-loader”. Style-loader integriše CSS string u <style> tag koji zatim ugradjuje u “head” HTML stranice. Instalacija ovih paketa se vrši sa naredbom:



Konfiguracija wabpack-a za CSS loadere

Unutar “webpack.congfig.js” fajla na postojeću konfiguraciju treba dodati sledeći kod:

NAPOMENA:
Obratiti pažnju da se u ovom nizu loaderi primenjuju u redosledu od poslednjeg ka prvom, stoga “style-loader”, treba da bude pre “css-loader”-a.

Webpack koristi regular expression definisano pod svojstvom “test” sa kojim pronalazi sve CSS fajlove, dok svojstvo “use” definiše koje sve loadere da koristi i kojim redosledom. Više o ovome pogledajte ona oficijelnoj stranici asset menagmente/css loaders. Ukoliko ipak ne želimo da ugradimo obradjeni CSS u JavaScript modul, to možemo postići preko webpack plugina pod nazivom ExtractTextWebpackPlugin

SASS loader

Instalacija paketa za SASS

Ukoliko koristimo SASS unutar nekog JavaScript modula (npr. import './scss/main.scss';), onda je potrebno instalirati pored neophodnih paketa vezanih za CSS: “style-loader” i “css-loader” i paket “sass-loader” koji transpilira SASS u CSS. Pored toga je potrebno instalirati i bibloteku (eng. library) za node.js vezanu za SASS node-sass:



Konfiguracija webpack-a za SASS loader

Kod konfiguracije SASS-a se i dalje koristi css-loader da parsira dobijeni css nakon transpiliranja SASS-a kao i style-loader koji ubacuje u takav css u style tag. Takodje se koristi sass-loader za transpilovanje:

Više o ovome možete pogledati na oficijelnoj stranici sass-loader

PostCSS

PostCSS je alat koji može da dodatno obradjuje i transformiše CSS ili SASS fajlove.

Instalacija PostCSS-a



Webpack konfiguracija za postCSS

Nakon instalacije je potrebno dodati 'postcss-loader' u wepack.config.js fajl, kao na sledećem primeru:

NAPOMENA:
Pošto se u nizu “use” loaderi primenjuju u redosledu od poslednjeg ka prvom, stoga je potrebno postcss-loader staviti posle sass-loader.

PostCSS dodaci

Dodavanje funkcionalnosti postCSS loaderu je omogućeno kroz instalaciju dodataka tzv. plugin-a. Postoji više od 200 regularnih plugina koji mogu da rade različite zadatke, neki od često korišćenih plugina su:

  • autoprefixer – dodaje prefikse vezne za odgovarajuće browser-e koristeći bazu podataka sajta Can I Use.


  • cssnext – omogućava da možemo da koristimo najnoviju CSS sintaksu, tako što transformiše novu CSS sintaksu u staru koja je kompatibilna sa starijim browserima. Tako da nije potrebno čekati podršku browsera. Postoji mogućnost da se instalira paket “cssnext”, medjutim prema zvaničnoj dokumentaciji uz “postcss” je preporučeno da se instalira ovako:


  • cssnano – minifikuje i formatira CSS kroz više optimizacija fa bi dobio što manju veličinu fajla za produkciju.


  • rucksack – dodaje nove mogućnosti za rad sa CSS-om, kao npr. responsive typography (font-size: responsive;), shorthand positioning syntax (position: absolute 0 20px;), native clearfix (clear: fix;), automatic font src generation (font-path: ‘/path/to/font/file’;), quantity pseudo-selectors (li:between(4,6))…


Listu ponudjenih plugina možete pogledati na oficijelnom sajtu kao https://github.com/postcss ili na postcss.parts katalogu.h

“postcss.config.js”

U okviru odvojenog fajla postcss.config.js se učitavju i konfigurišu instalirani dodaci:

Babel

Babel je JavaScript transpiler, koji od novih verzija JavaScripta “pravi” kod prema ES5 standardu. Sastoji se od:

  • babel-core je glavni babel-ov npm package
  • babel-loader je modul koji služi da poveže “Babel” i Webpack

Instalacija paketa za Babel

Instalacija webpack-a se vrši sledećim naredbama:



Konfiguracija webpack-a za babel

Babel preset

Da bi smo mogli da koristimo pripremljena setovanja (eng. preset), potrebno je da ih instaliramo.
Na ovome primeru ćemo da koristimo “env” preset paket koji se instalira sa babel-preset-env, a koristi se da transpilira javascript kod pisan prema verziji koja je aktuelna u trenutnoj godini (ES2017). Možemo da izaberemo i drugi preset za standard definisan u drugoj godini (npr. ES2016), ali je potrebno da koristimo odgovarajući paket za instaliranje babel-preset-es2016. Više o ovim paketima možete pogledati u dokumentaciji na oficijelnom sajtu babeljs.io/docs/plugins/#presets-official-presets



Pored instalacije potrebno je napraviti fajl pod nazivom .babelrc, koji se stavlja u root projekta. Ovaj fajl je zadužen da čuva babel-ova podešavanja, stoga je potrebno unutar tog fajla ubaciti sledjeći konfiguracioni JSON:

Više o instalaciji Babel-a možete pogledati na oficijelnoj stranici babeljs.io

Image loader

U HTML dokumentu browser povlači slike sa servera tek kada “naleti” na <img> tag (ili ne element koji ima svojstvo “background-image”). Koristeći webpack možemo optimizovati slike pa čak ih i sačuvati kao binarni podatak unutar JavaScript-a. Slike ubačene u javascript možemo da preload-ujemo pa ih browser neće morati da ih preuzme sa dodatnim request-om.

Instalacija paketa za img loader



Konfiguracija webpacka za image loader

“image-webpack-loader” kompresuje sliku, nakon čega “url-loader” takvoj kompresovanoj slici proverava veličinu. Ukoliko je slika manja od željene veličine (veličina se definiše kroz options), “url-loader” je sa “Base64 encoding”s prebacuje u šifrovani binarni padatak koji takav ubacuje direktno u JavaScript modul. Ukoliko je kompresovana slika ipak veća od definisane, onda je webpack ubacuje u dist folder kao odvojeni fajl.

Korišćenje

Za korišćenje slika u JavaScript modulu je potrebno da ih importujemo:

Kao što se vidi slike su smeštene u assets folderu, koji se nalazi u root-u projekta. Nakon importa možemo da sa slikama radimo šta god je potreba, npr. jednostavno prikazivanje:

Nakon build-ovanja projekta sa webpack-om u sklopu dist foldera za produkciju se pojavljuje samo velika slika (jer nije prošla test limit 10000), dok je mala slika ugradjena kao binarni string u JavaScript modul.

eslint-loader

ESlinter je jedan od najpopularnijih lintera koda, standardizuje izgled JavaScript koda i nalazi greške odmah pri kompajliranju.

Instalacija

Potrebno je prvo instalirati “eslint core”:



Nakon čega možemo da instaliramo i “eslint-loader”:



Više o eslint-loader možete pročitati na oficijelnoj stranici plugina github.com/MoOx/eslint-loader

Konfiguracija u webpack.config.a

NAPOMENA:
Obratite pažnju na redosled učitavanja loadera, potrebno je da ovaj loader bude pre (čitaj: “ispod”) babel-loader inače će lintovanje biti nad transpilovanim kodom. Postoji mogućnost da se doda properties enforce: "pre" koji mu omogućava da ovaj loader prvi učita bez obzira na mesto u kodu.

Konfiguracija .eslintrc.json

Potrebno je kofigurisati i sam eslint core. Konfigurisanje se čuva u .eslintrc.json fajlu.

Primer konfigurcije

Sekcija “extends” definiše koji set pravila želimo da primenimo. Možemo da koristimo preporučeni set od eslint-a “eslint:recommended” ili instalirati neki drugi npr. “airbnb”
U sekciji “rules” može da se pojedinačno promeni neko od pravila. Listu svih pravila možete pronaći na oficijelnom sajtu u sekciji “rules”. Dovoljno je izabrati neko pravilo i promeniti mu konfiguraciju.

Primer – deaktiviranje pravila

Ukoliko ne želimo da bude primenjeno neko pravilo dovoljno je da u sekciji “rules” ubacimo kod "nazivPravila": 0, pa ako ne želimo da aktiviramo proveru pravila “no-console”:

Enabling sourcemap

SourceMap je fajl unutar koga je mapirana je veza izmedju koda u krajnjem fajlu prikazanog u browseru i originalnih ne-bundlovanih fajlova (koji još nisu transpilovani, minifikoavani, kompresovani,…). Sourcemap omogućava pregledaču da rekonstruiše početni izvor i prikaže rekonstruisan originalni kod u debugeru, iako se on ustvari nalazi okviru krajnjeg minifikovanog i bundl-ovanog fajla.

JavaScript sourcemap

Za aktiviranje ove funkcionalnosti nije potrebno instaliranje nekog paketa, već je dovoljno definisati odredjene parametre u sklopu webpack.config.js fajla:

Nakon ovoga u inspektoru web browser-a će svaki kod biti linkovan sa svojim početnim (originalnim) fajlom pre bundl-ovanja, a ne za bundle.js. Više o ovome možete pročitati na oficijelnom sajtu pod sekcijom using source maps.

CSS & SCSS sourcemap

Najčešća organizacija u radu sa SASS-om je da se svi fajlovi importuju u jedan Da bi aktivirali ovu mogućnost za CSS ili SCSS fajlove, potrebno je dodati kod SCSS/CSS loadera options:{sourceMap: true} kod svakog loadera:

Nakon ovoga u inspektoru web browser-a će svaki elemenat biti linkovan sa svojim početnim (originalnim) SCSS fajlom pre bundl-ovanja, a ne za main.css.

Webpack dodaci (plugin)

Webpack ima svoje plugin-e preko kojih može da proširi svoje funkcionalnosti. Listu dostupnih plugin-a za webpack 2 možete pogledati na oficijenoj stranici https://webpack.js.org/plugins/.

ExtractTextWebpackPlugin

Već smo u predthodnom delu videli kako se CSS fajl jednostavno integriše u JavaScript, a ova tehnika je odlična kada želimo da pravimo “reusable” komponente koje bi se više puta ugradjivale u različite delove programa. Medjutim nije dobro primeniti ugradjivanje stilova u javascript za sve stilove aplikacije, jer bi stilovi bili učitani kada i javascript a to je najčešće tek nakon učitavanja DOM-a. To je neprihvatljivo sa stanovišta “user experience”, jer bi korisniku u jednom trenutku bio prikazan nestilizovani sadržaj.
ExtractTextWebpackPlugin je webpack plugin koji omogućava da se obradjeni CSS (transpilovan, minifikovan…) ipak izdvoji u zasebni fajl. Tako izdvojeni CSS fajl može da se jednostavno ugradi u vrh HTML-a kroz link tag, što će nam obezbediti da se stilovi učitaju pre DOM-a. Ovakva tehnika nam omogućava da korisnik odmah nakon učitavanja
DOM-a vidi stilizovan sadržaj.

Instalacija ExtractTextWebpackPlugin



Konfiguracija webpack-a za ExtractTextWebpackPlugin

Nakon instalacije potrebno je ažurirati konfiguracioni fajl webpack.config.js. Prvo je potrebno na vrhu fajla importovati plugin modul:

Nakon čega je potrebno editovati deo vezan za CSS i zameniti “style-loader” sa pluginom:

Takodje je slično potrebno uraditi kod SCSS dela:

Pored navedenih izmena potrebno je dodati novu sekciju u webpack.config.js vezanu za plugin, koja “obaveštava” webpack koji plugin koristimo i u koji fajl želimo da izdvojimo CSS iz JavaScript fajla bundle.js.

Nakon startovanja webpack-a, pojaviće se novi fajl “style.css”, koji je potrebno ubaciti u našu HTML stranicu:

HtmlWebpackPlugin

Namena ovog dodatka je da generiše novi HTML fajl na osnovu nekog template-a, koji se dinamički povezuje sa generisanim “bundle.js” fajlom.

Instalacija HtmlWebpackPlugin



Konfiguracija webpacka za HtmlWebpackPlugin

Potrebno je na vrhu fajla importovati zahtevani plugin:

A zatim u delu za vezan za plugine pored prethodno ubačenog ExtractTextPlugin, dodati i ovaj:

Nakon startovanja webpacka-a on će generisati novi HTML fajl index.html, koji u sebi već ima ubačene veze do dva takodje generisana javascript fajla:

Generisanje HTML-a na osnovu template-a

Prethodno generisani HTML fajl osim što ima inkludovane skripte i nije naručito interesantan. Stoga je potrebno napraviti HTML template sa odgovarajućim sadržajem, koji ćemo da iskoristimo za generisanje novog HTML-a. Povezivanje templeta i odgovarajućeg JavaScript fajla se vrši definisanjem objekta koji se prosledjuje kao parametar HtmlWebpackPlugin pluginu. Kroz taj objekat se definiše ime i putanja HTML template-a koji se koristi, kao i naziv odgovarajućeg bundlovanog JavaScript dela.

Medjutim najverovatnije je da se aplikacija ne sastoji samo iz jednog HTML fajla tj. stranice, nego iz više njih, stoga je potrebno da drugačije organizujemo projekat i promenimo konfiguraciju plugina.
Napravićemo novi folder u okviru src foldera koji ćemo da nazovemo “views”, a koji je zadužen da se u njemu čuvaju template HTML stranice. Unutar njega možemo da napravimo dve HTML stranice koje ćemo nazvati “index.html” i “druga.html”. Nakon ovoga je potrebno da promenimo trenutnu konfiguraciju za HtmlWebpackPlugin u okviru webpack.config.js fajla:

Više o ovome pluginu pogledajte na webpack sajtu pod sekcijom “Setting up htmlwebpackplugin”, ili na samoj stranici plugina “Html webpack plugin”, dok se o gotovim templejtima možete pročitati na https://github.com/jaketrent/html-webpack-template.

CleanWebpackPlugin (čišćenje /dist foldera)

Pri svakom menjanju imena fajlova koji treba da se generišu, u folderu za produkciju će se generisati novi preimenovani fajlovi. Medjutim fajl sa starim nazivom neće nestati, iako je nepotreban, tako da se pravi gomila nepotrebnih fajlova u produkcijskom folderu. Iz toga razloga nam je potrebno brišemo stare fajlove pre generisanja novih, pa ćemo da koristimo plugin pod nazivom “clean-webpack-plugin”. Ovaj dodatak pri svakom stratovanju webpack-a, prvo briše sadržaj celog produkciskog foldera “dist”, a zatim generiše nove fajlove u njega.

Instalacija CleanWebpackPlugin



Konfiguracija CleanWebpackPlugin

Prvo je potrebno na vrhu stranice importovati instalirani plugin

A zatim u delu za vezan za plugine dodati i ovaj:

Više o ovome plugin-u pogledajte na oficijelnoj stranici pod sekcijom Cleaning up the dist folder.

CommonsChunkPlugin

Ukoliko aplikacija ima dosta “third party dependencies”, preporuka vezana za optimizaciju aplikacije je da se “third party dependencies” izdvoje u tzv. “Vendor Chunk”. Ovo se radi iz razloga što želimo da browseru omogućimo da kešira ove podatke jer se oni za razliku od našeg koda retko ažuriraju. Ovaj postupak smo već obradili u sekciji “Definisanje višestrukih ulaznih i generisanje više izlaznih fajlova”, stim što ćemo u sada u drugi bundle file da ga nazovemo “vendor” a u njega ćemo da stavimo sve “third party dependencies” a koji se nalaze u package.json fajlu pod “dependencies”. Više o ovome pročitajte na oficijelnom sajtu u sekciji “explicit-vendor-chunk”.

Primer

U ovome primeru je generisan jednostavni Vue.js projekat webpack-simple, u sklopu koga je “vue” kao third party dependencies.

Prethodni kod će da generiše dva fajla: app.bundle.js i vendor.bundle.js, pa će browser moći da kešira vendor.bundle.js. Medjutim pri ovom postupku se pojavio probem da su sada ta dva fajla u zbiru veća nego početni bundle.js fajl pre razdvajanja. Ovo se dešava jer je u ulaznom fajlu index.js na vrhu fajla “ručno” importvan neki od “third party dependencies”, pa je došlo do dupliranja.
Rešenje problema dupliranja je plugin pod nazivom “CommonsChunkPlugin”.

Konfiguracija CommonsChunkPlugin

Ovaj plugin je već ugradjen u sam webpack stoga ga ne treba instalirati, potrebno ga je samo konfigurisati.

Ovakva konfiguracija govori webpack-u da proveri da li unutar “entry” fajlova (u ovome slučjau “app” i “vendor”) postoji neki paket koji se nalazi u oba fajla, i ako postoji da ga stavi samo u onaj pod nazivom “vendor”.

UglifyjsWebpackPlugin

Glavna namena ovoga plugina je minifikacija JavaScript-a.

Instalacija



Konfiguracija

Da možemo da konfigurišemo, prvo je potrebno da importujemo plugin na vrhu webpack.config.js fajl.

Nakon čega možemo u sekciji zaduženoj za plugine dodamo i ovaj plugin:

Prethodna konfiguracija će biti stalno aktivna, medjutim šta ako želimo da se minifikuje samo kada se build-uje za produkciju – onda se koristi uslov:

Više o ovome pluginu pročitajte na oficijelnom sajtu u sekciji UglifyjsWebpackPlugin, a o setovanju process.env.NODE_ENV promenjive pogledajte u istoimenoj sekciji “process.env.NODE_ENV”

Dinamičko učitavanje JavaScript modula

Dinamičko učitavanje modula podrazumeva da se u okviru jednog JavaScript fajla, dinamički (nakon akcije korisnika) poziva izvršavanje drugog JavaScript fajla. Ceo sistem se zasniva na specifičnom delu koda koji se nalazi u okviru početne stranice iz koje se zahteva učitavanje novog modula, a bavi se dinamičim učitavanjem modula:

Nakon izvršavanja ovog dela koda u stranicu se učitava i dodani JavaScript modul.
Sintaksa “System.import()” nam omogućava dinamičko i kondiciono učitavanje jednog modula. Pisana je prema ES2015 standardu i bazirana je na Promisima stoga vraća Promise.

Više o ovome možete pročitati u specifikaciji ES2015 standarda.

process.env.NODE_ENV

Kada koristite neki od framework-a tokom razvoja aplikacije, framework je stalno na oprezu i vrši česte provere da bi vam pružio potrebna upozorenja kako bi vam pomogao u pogledu uobičajenih grešaka i zamki. Međutim, to postaje beskorisno u krajnjoj produkciji a povećava veličinu aplikacije pri učitavanju i samo opterećuje aplikaciju. Produkciski režim u framework-u se definiše preko promenjive “process.env.NODE_ENV”, stoga je bitno da u wepack-u definišemo ovu promenjivu kada želimo da pošeljemo aplikaciju na produkciju. Za ovu namenu se koristi webpack-ov DefinePlugin, jer on omogućava da se setuju promenjive operativnog sistema. Da bi mogli da koristimo ovaj webpack-ov plugin u okviru “webpack.config.js” fajla, neophodnoje da ga prvo importujemo u fajl:

Zatim da preko ovog plugina da definišemo promenjivu ‘process.env.NODE_ENV’:

Medjutim nepotrebno je da i pri radu u lokalu sa dev serverom, promenjiva “process.env.NODE_ENV” bude setovana na “production”, stoga je potrebno promenjivu definisati programabilno. To se postiže tako što drugačije konfigurišemo DefinePlugin:

Sa prethodnim kodom smo rekli webpack-u da će sistemska promenjiva “process.env.NODE_ENV” biti ista kao što je promenjiva u našem lokalnom sistemu. A definisanje te promenjive u lokalnom sistemu se vrši konfiguracijom kroz “scripts” svojstvo u okviru fajla “package.json”:

Ukoliko ne koristimo “UglifyjsWebpackPlugin” možemo da monifikujemo kod produkcije dodavanjem flag-a -p:

Završna verzija projekta uz članak

Svi primeri iz članka objedinjeni su u jedan projekat. Završna verzija projekta je objavljena na GitHub-u
https://github.com/choslee/webprogramiranje-webpackOsnove.

webpack.config.js

package.json



Uvod u modularno programiranje

Karakteristike modularnog programiranja

modularno programiranje kocka

Modularni koncept programiranja je trenutno jedan od najzastupljenijih koncepata u modernom programiranju sa Javascript-om. Zasniva se na razbijanju jedne velike aplikacije na manje, enkapsulirane, nezavisne delove koda – module. Cilj modularnosti jeste da se smanji kompleksnost prema principu “podeli pa vladaj”. Najznačajnije prednosti modularnog programiranja su:

  • preglednija i razumljivija aplikacija
  • lakše kontrolisanje domena promenjivih
  • sprečeno “zagadjenje globalnog domena”
  • ponovna upotrebljivost koda
  • mogućnost rada na istom projektu više različitih timova ili programera koji rade odvojene manje zadatke.
  • lakše debagovanje

Tipovi sintakse

U zavisnosti od verzije JavaScripta postoje različiti pristupi problemu i različite sintakse koje omogućavaju modularno programiranje:

Native ES5 sintaksa

U vreme pisanja JavaScripta, modularno programiranje nije bilo planirano kao način programiranja, tako da JavaScript sve do ES6 (ES2015) verzije nema podršku za module. U okviru native ES5 se koriste razni šabloni (eng. patern) koji imaju sličnosti sa modularnim programiranjem ali nemaju baš sve karakteristike “čistokrvnog modularnog šablona”.
Pročitajte više o native ES5 sintaksi u članku Modularno programiranje sa ES5.

ES5 sa externim bibliotekama

Upravo zbog nedostatka podrške za module u ES5 su kreirane externe syntax-e koje jeziku daju nedostajuću syntax-u:

  • CommonJS Modules
  • Asynchronous Module Definition (AMD)
  • Universal modul definition (UMD)

Pročitajete više o sardnji ES5 sa externim bibliotekama u članku Modularno programiranje – eksterna sintaksa (AMD & CommonJS).

ES6 modules sintaksa

Uz ES6 stiže podrška za modularni sistem ugradjena i u sam jezik – ECMAScript 6 modules. Nova syntax-a još uvek nije podržana od strane browser-a, pa je potrebno koristi Babel ili TypeScript da bi se transpilovala u ES5.
Pogledajte više o novoj ES6 modules sintaksi u članku Modularno programiranje sa ES6.

Modul loaders i bundlers

Ukoliko se odlučimo za modularni način programiranja aplikacije, susrećemo se sa problemom organizovanja učitavanja modula i modula od kojih oni zavise tzv. dependencies. Modul loaders i bundlers su nepohodna podrška programeru za jednostavniji i efikasniji rad sa modulima.

Modul loaders

“Modul loaders” omogućavaju dinamičko učitavanje modula pazeći na raspored učitavanja. Najpoznatiji modul loader-i su:

  • RequireJS – implementira AMD modularni sistem.
  • SystemJS – je univerzalni loader i može učitavati module u bilo kom popularnom formatu (CommonJS, UMD, AMD, ES6). Najčešće se koristi zajedno sa jsmp.io koji je više od običnog package manager-a, pa može transpilovati ES6 kao i TypeScript ili CoffeeScript.

Šta su CommonJS, AMD i UMD?

  • Asynchronous Module Definition (AMD) kao što mu i samo ime kaže, podržava asihrono učitavanje modula što je pogodno za rad sa modulima u browser-u.
  • CommonJS učitava module sinhrono i zbog toga se najčešće koristi za rad sa modulima na serverskoj strani u okruženju node.js. Iako nije planiran za rad u browser-u, uz pomoć “modul bundler-a” je moguće da prilagoditi CommonJS radu u browser-u.
  • Universal module definition (UMD) je kompatibilan i sa AMD i sa CommonJS definicijom i koristi se uglavnom ukoliko ima potrebe da se isti modul učitava na serveru i u browser-u.

Više p ovome pogledajte u članku Modularno programiranje – eksterna sintaksa (AMD & CommonJS)

Module bundlers

“Modul bundlers” rešavaju problem tako što kompajliraju sve module u jedan fajl prema odredjenom redu pazeći da neki modul koji je zavisnost drugome bude učitan na vreme. Najpoznatiji modul bundler-i:

  • Browserify – implementira CommonJS i u browser okruženju. Može da se nadogradjuje raznim plugin-ima i uz pomoć task runner-a (Gulp ili Grunt) može da završi različite poslove.
  • Webpack – može učitavati module u bilo kom popularnom formatu (CommonJS, UMD, AMD, ES6), dolazi kao jedan paket i nisu mu potrebni dodatni plugin. Pored učitavanja modula može da izvrši posao transpilovanja ES6(Typescript) u ES5 kao i transpilovanje SASS-a u CSS.

Zaključak

Izbor tipa podrške za rad sa modulima najviše zavisi od same aplikacije. Stoga ukoliko se aplikacija sastoji od manjeg broja većih modula, smatra se da je bolji izbor “modul loader”, dok se smatra da je “modul bundler” pogodniji ukoliko se aplikacija sastoji od većeg broja manjih modula. U svakom slučaju najsigurnije je isprobati oba tipa pa videti koji daje bolje rezultate.

Popularnost alata za rad sa modulima tokom zadnjih par godina se može proučiti na grafikonu google trends. Na osnovu grafikona se zaključuje da postoji ogromno interesovanje za Webpack kao predstavnika modul bundler-a, dok kod predstavnika modul load-era je vidljiv rast popularnosti SystemJS-a.


Front-End alati (pregled)

Package manager

Uvod

frontend

Današnji web development se zasniva na skupu različitih tehnologija koje se istovremeno koriste na jednom projektu, kao što je: Typescript (jezik), Sass (CSS ekstenzija), JSCS (code style linter and formater), JSHint ili TSLint (alat za detekciju grešaka i potencijalnih problema), BrowserSync (alat za sihronizaciju promena u kodu i u browser-u – Live reload )… Za jedan projekat je potrebna instalacija svakog od navedenih programa ali i svih programa (dependencies) od kojih zavise isti a prethode njihovoj instalaciji.
Package manager eliminiše potrebu za ručnim instaliranjem softvera sa svim neophodnim pratećim softverom (dependencies) kao i za ažuriranje svih tih komponenti. Package manager je skup alata zaduženih da automatizuju procesa instaliranja, konfigurisanja i uklanjanja programa. Package manageri rade sa packages tj. sa softwerom ili podacima koji se nalaze u arhivnim fajlovima. Package sadrže meta-data koji opisuju dati softver kao što je naziv softvera, verzija i lista svih dependencies (drugi softver neophodan za rad datog sofvera). Najpoznatiji package menadžeri su:

  • npm
  • bower
  • JSPM (JavaScript Package Manager)
  • Yarn

Continue reading…