20
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.

Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

Embed Size (px)

Citation preview

Page 1: Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

Cesta k WebpackuSkrz 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.

Page 2: Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

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.

Page 3: Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

-1.5 roku

Posuňme se v čase do ledna 2014…

Page 4: Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

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.

Page 5: Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

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.

Page 6: Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

-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.

Page 7: Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

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.

Page 8: Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

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.

Page 9: Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

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á.

Page 10: Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

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.

Page 11: Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

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).

Page 12: Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

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.

Page 13: Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

-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…

Page 14: Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

…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.

Page 15: Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

-3 měsíce

WebpackS 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.

Page 16: Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

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.

Page 17: Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

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.

Page 18: Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

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š.

Page 19: Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

<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.

Page 20: Cesta k Webpacku (Skrz DEV Cirkus, 10.6.2015)

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čí.