Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

Webpack je nástroj, který prostě musíte mít, pokud to s vývojem frontendu myslíte vážně. Používáme ho ve @SkrzCz na všech projektech. Než jsme se k němu dostali, prošli jsme si slepými cestičkami. Co jsme udělali dobře, co špatně, co nám pomohlo.

  • Login to see the comments

  • Be the first to like this

Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

  1. 1. Cesta k Webpacku Skrz DEV Cirkus 10.6.2015 V Node.JS na server-side existuje jednoduchý způsob, jak začít používat cizí knihovny - `npm install …` a poté stačí napsat `require(“…”)`. Pokud jde o browser, už to tak jednoduché není. a) Žádný standardní způsob není. b) “assety” před tím, než se pošlou do prohlížeče je potřeba minifikovat (odstranit bílé znaky, komentáře, zkrátit názvy proměnných). c) A nakonec optimalizovat pro posílání přes HTTP (spojit soubory dohromady), upravit názvy, aby podporovaly dobré kešování apod. V této prezentaci probereme, jak jsme ve Skrzu došli k Webpacku.
  2. 2. Co je Skrz? Skrz je katalog akčního zboží, takový online akční leták. Musí poskytovat skvělou user experience pro své uživatele, tudíž se řeší nejrůznější Javascriptové vychytávky, animace, AJAXová volání. Ale zároveň musí být obsah dostupný pro vyhledávače - tzn. že renderovaní se musí řešit na serveru.
  3. 3. -1.5 roku Posuňme se v čase do ledna 2014…
  4. 4. Takhle nějak vypadal Javascriptový kód. Moc a moc úrovní odsazení, drlouhá jQuery špageta. Jestli uživatel je/není na nějaké stránce se neřešilo, vždycky se posílal stejný Javascript, který obsahoval vše - rozlišení, jestli se nějaký kód má, nebo nemá aplikovat na dané stránce se řešilo přes různé ověřování, jestli existují elementy pro všechny selektory, které daná fcionalita potřebovala = hromada ifů. Výsledkem bylo dlouhé načítání, špatný výkon.
  5. 5. Takhle nějak vypadalo řešení knihoven a závislostí. Podobně to asi někdy řešil každý. Na produkci se akorát zapla proměná `settings.user_compile_js`. Minifikaci a spojování Javascriptových souborů řešila vlastní skript v PHP - tzv. packer. Tomu se v packer.conf souboru určilo, co všechno za soubory má projít a jak je má spojit. Často je spojil špatně, vyhodil znaky, které neměl apod. - takže až na produkci se zjistilo, že kód nefunguje.
  6. 6. -1 rok LiveScript + LESS + AMD Grunt + Require.JS Březen až květen 2014 se řešil redesign Skrzu. Ještě důležitější než nový kabátek to ale znamenalo přepis hodně stránek a nové technologie. Místo home-made frameworku Ulriky se začala na backendu používat Symfony. Většina stránek začala brát data z Elasticsearch místo MySQL. A na client-side místo čistého Javascriptu se začal používat LiveScript, místo čistého CSS zase LESS. Nejdůležitější však bylo strukturování LiveSciptu do AMD (asynchronous module definition) - což dalo dobře základ škálovatelnému řešení rozdělení závilostí. Celé to spojoval Grunt a Require.JS.
  7. 7. NO-FRAMEWORK framework aka vlastní řešení Jak jsem říkal, jako požitek pro uživatele je pro Skrz důležitá přístupnost pro roboty - tzn. renderování musí probíhat na serveru. V době redesignu byly v Javascriptu nejpoužívanější frameworky Backbone a Angular. Oba se snažily řešit renderování na klientu a podpora pro renderování na serveru bylo nedostatečná. A pokud se odmyslelo renderování na klientu, přidávaly pramálo užitečného pro strukturování aplikace - neřešili problém, který jsme měli ve Skrzu. My pouze potřebujeme vyrenderované HTML na serveru “rozhýbat” na klientu, sem tam načíst něco AJAXem. Vznikl tedy vlastní způsob, jak stránku rozděli na komponenty a jak je zaregistrovat a napojit na ně Javascript.
  8. 8. V aplikaci je jeden entry point - main.ls (tečka LS protože LiveScript). V elementu s ID js-options je serializovaný JSON s proměnnými týkajících se celé stránky předaných z backendu. Obsahuje např. ID uživatele. V Javascriptu používáme dependency injection container. V této ukázce je to LillyContainer (Lilly je název jedné z aplikací). Ten se předá aplikaci a pak se aplikaci prožene fronta událostí, které do té doby na stránce vznikly.
  9. 9. Dependency injection container obsahuje metody s prefixem “get” a “create”. “get” metody znamenají, že daná instance je singleton - bude vytvořena pouze jednou za celý běh aplikace. “create” metody jsou továrničky. Nejvíce pro tzv. “widgety” - to jsou právě jednotlivé části, na které je stránka rozdělená.
  10. 10. Na začátku každé stránky (hned po otevíracím tagu <body>) se inicializuje kód fronty událostí. Něco jako mají trackovací kódy Google Analytics (_gaq), Facebooku (_fbq). Cílem (ke kterému jsme se bohužel ještě nedostali), je mít jeden `<script … async>` v hlavičce, eventy tedy hned po načtení části stránky mohou hned napárovat Javascriptovvý widget (který se stará o interakci) na naparsovaný DOM element.
  11. 11. Události jsou prostě kusy Javascriptu přímo v HTML, které zavolají vždy nějakou metodu na globalním objektu `skrz`. Nejdůležitější/nejpoužívanější je událost/metoda `skrz.widget(…)`. Jako první argument má název widgetu (přilepte na začátek “create” a na konec “Widget” a máte název metody z dependency injection kontejneru) a jako druhý argument model v JSONu. Mohli jste si všimnout, že v kontejneru mají ještě widget-továrničky parametr `el`. To je odkaz na parent DOM element, ve kterém je volání `skrz.widget` (ten se odvodí auto-magicky díky způsobu, jakým prohlížeče zpracovávají DOM).
  12. 12. Wiget je třída, v konstruktoru dostane tři argumenty `el`, `model` a `children`. `el` je element, ve kterém byla vyvolána eventa `skrz.widget`. `model` je druhý argument předaný události. Např. máme `ItemWidget` a ten má v model ID nabídky. Widgety tvoří strom, podobně jako DOM tvoří strom. `children` jsou widgety podřazené současnému. Lifecycle widgetu je jednoduchý - po vytvoření kontejnerem se zavolá metoda `bind`. Ta se stará o interaktivitu. Najde si různé elementy. Máme konvenci, že elementy používané z Javascriptu musí mít třídu s prefixem `j- *`. Opakem `bind` je `unbind`, která se zavolá, pokud je widget odstraňován. Widgety tvoří zapouzdřené celky, které se jednoduše upravují. Starají se pouze přidání interaktivity nad HTML renderované na serveru.
  13. 13. -0.9972 roku Require.JS Widgety byly super a rozdělení do více souborů jakbysmet. Ale asi za 1 den, když se mělo natáhnout třeba 30 souborů s 30 různými widgety, jsme narazili na problém s připojením k internetu v předchozích kancelářích Skrzu na Bohdalci - prostě se nestáhl jeden z 30 souborů a najednou tu byl těžko debugovatelný problém. Vykašlali jsme se tedy na Require.JS…
  14. 14. …a místo něj se inspirovali Stichem (https://github.com/ sstephenson/stitch), což je Node.JS middleware, který spojí X JS souborů dohromady. Přepsali jsme řešení do PHP a donedávna úspěšně používali. Mělo to však několik problémů: a) Stitch nechápal Javascript, pouze vzal soubory v adresáři, dal kolem každého boilerplate a spojil je dohromady. Výsledkem bylo mnoho opakujících se řetězců s názvem modulu, a tdy zbytečně velká velikost souboru. b) Řešil pouze Javascript (resp. LiveScript), na LESS jsme používali stále Grunt tasky.
  15. 15. -3 měsíce Webpack S velkou popularitou a množstvím přednášek ohledně ReactJS, jsem narazil na přednášku od Pete Hunta, který ve Facebooku řeší Instagram webový frontend. Facebook používá k minifikaci, modularizaci a servírování Javascriptu adaptabilní a prediktivní řešení. Je ale hodně provázané s celým zbytkem Facebooku. Pro Instagram potřebovali něco daleko jednoduššího, co by mohli rychle nasadit. A našli Webpack.
  16. 16. Google -> “pete hunt webpack howto” Zadejte do Google “pete hunt webpack howto” = nejjednodušší způsob, jak s Webpackem začít. A taky, jak jsme začali my.
  17. 17. package.json: {
 "name": "Lilly",
 "version": "1.0.0",
 "devDependencies": {
 "webpack-dev-server": "^1.7.0",
 "webpack": "^1.7.3"
 },
 "dependencies": {
 "LiveScript": "^1.3.1",
 "bootstrap": "^3.3.2",
 "bootstrap-webpack": "0.0.3",
 "css-loader": "^0.9.1",
 "expose-loader": "^0.6.0",
 "extract-text-webpack-plugin": "^0.3.8",
 "file-loader": "^0.8.1",
 "font-awesome": "^4.3.0",
 "font-awesome-webpack": "gowravshekar/font-awesome-webpack#e22214a",
 "imports-loader": "^0.6.3",
 "jquery": "^1.11.2",
 "less": "^2.0.0",
 "less-loader": "^2.0.0",
 "livescript-loader": "^0.1.3",
 "source-map-loader": "^0.1.3",
 "style-loader": "^0.8.3",
 "uglify-js": "^2.4.16",
 "url-loader": "^0.5.5",
 "webpack": "^1.5.3"
 },
 "scripts": {
 "dev": "BUILD_DEV=true webpack-dev-server -d --hot --inline --progress --colors --port 8443 --inline --https --content-base is/ -- output-public-path https://localhost:8443/assets/",
 "build": "npm run build:queue && webpack -p --progress --profile --colors",
 "clean": "rm -f www/assets/*",
 "build:queue": "lsc --compile --bare --print client-src/Skrz/Inlined/queue.ls | uglifyjs --compress --mangle > client-src/Skrz/Inlined/ queue.js"
 }
 }
 V praxi to vypadá následovně: v package.json máme natažené všechny potřebné závislosti. A pak už 2 důležité scripty: a) `dev` spustí Webpack dev server - NodeJS server, který poslouchá na určitém portu. Umí livereload stránky při změně a další vychytávky. Nebuďte líní, pokud budete s webpackem začínat, si dev server nastavit. b) `build` spojuje a minifikuje soubory pro použití na produkci. V případě skrzu to trvá několik minut, ale výsledek je skvělý. Že je pro nás webpack tak skvělý je hlavně díky rozhodnutí strukturovat kód pomocí AMD modulů. Ale stejně tak webpack podporuje synchronní `require` jako je v Node.JS a další způsoby.
  18. 18. webpack.config.js: var webpack = require("webpack");
 var ExtractTextPlugin = require("extract-text-webpack-plugin");
 
 module.exports = {
 entry: {
 all: ["./client-src/Skrz/Bundle/LillyBundle/main"]
 },
 output: {
 path: "./www/assets",
 publicPath: "/assets/",
 filename: "all.js"
 },
 module: {
 loaders: [
 { test: /bootstrap/js//,
 loader: ‘imports?jQuery=jquery' },
 { test: /.ls/,
 loader: “livescript-loader" },
 { test: /.css$/,
 loader: ExtractTextPlugin.extract("style-loader", “css-loader") },
 { test: /.less$/,
 loader: ExtractTextPlugin.extract("style-loader", “css-loader!less-loader") },
 { test: /.(woff2?|ttf|eot|svg|jpg|png|gif|swf)(?.*)?$/,
 loader: “file-loader" }
 ]
 },
 resolve: {
 extensions: ["", ".js", ".json", ".ls"]
 },
 plugins: [
 new webpack.DefinePlugin({
 __DEV__: JSON.stringify(JSON.parse(process.env.BUILD_DEV || "true"))
 }),
 new ExtractTextPlugin("[name].css")
 ]
 
 };
 Webpack se nastaví přes `webpack.config.js`. Důležitá je sekce loaders, kde podle přípony souboru nastavíte, co v tom souboru je. Např. tady soubory končící na `.ls` se interpretují jako LiveScript. Soubory končící na `.less` jako LESS = ano, uděláte v Javacriptu `require(“./style.less”)` a webpack soubor přeloží do CSS, všechny CSS pak posbírá ExtractTextPlugin (viz sekce plugins) a vytvoří jeden CSS soubory se vším. Zahodili jsme tedy Grunt a o všechno se teď stará webpack. Ten navíc např. fonty referencované v CSS, obrázky uloží do souboru, který má jako název MD5 hash obsahu a nahradí všechny výskyty v CSS - můžete tedy nastavit na fonty/obrázky kešování forever, a pokud se změní, vznikne nový soubor, a tím pádem se invaliduje keš.
  19. 19. <head>: <link rel="stylesheet" href="{$assetsBaseUrl}/all.css"> za <body>: <script>{$queue nofilter}</script>
 <script>skrz.begin();</script> před </body>: <script>skrz.end();</script>
 <script>skrz.widget("Body");</script>
 <script type="application/json" id=“js-options”>{$options|json_encode nofilter}</script>
 <script src="{$assetsBaseUrl}/all.js"></script> Pak už jsem stačí nastavit `$assetsBaseUrl` v šabloně podle toho, jestli se jedná o vývoj nebo produkci.
  20. 20. Otázky? Díky! Jedna otázek byla: “Proč použít Webpack a nepoužít Gulp?” A proč nepoužít obojí! Každá věc je totiž na něco jiného. Webpack se stará o přípravu “assetů” pro development/produkci. Gulp je task runner. Jedním z těch tasků může být např. spuštění Webpacku pro development nebo vybuildovaní assetů pro produkci. My jsme Gulp jako task runner nepotřebovali, protože většinu tasků máme v PHP, v Javascriptu máme pouze ty, které byly vidět v `package.json` na jednom z předchozím slajdů. `npm run …` nám na to úplně stačí.

×