Na obsah stránky

Pod Vocasem

Aleš Roubíček |

Nedávno mě Džoukr oslovil, zda bych jim neudělal kašpárka v jejich podcastu Pod Vocasem. Konkrétně, zda bychom si mohli popovídat o Clojure. Pokud jde o příležitost popovídat si s Románkem osobně, tak to jsem si nemohl nechat ujít. No a celé si to můžete poslechnout tady:

Google Analytics bez cookies

Aleš Roubíček |

Máme tu rok 2022 a weby jsou plné “cookie lišt“ a jiných podivných popupů. Ačkoliv nutně nemusí. Ne, že by nemuseli uživatele informovat o tom, že se ho chystají špehovat na každém kroku – a tato data prodávat dál. To je samozřejmě k uživateli fér. Nemusí ale uživatele za každou cenu špiclovat.

Já vím. Všichni si ale chceme honit pindíky nad erektivními grafy v Google Analytics. I mě zajímá, kolik lidí si tento článek přečte. Nechci u toho pomáhat Googlu k získávání dat, která mu na mým webu můžou být ukradená… Jak vidíte, žádnou cookie lištu nevidíte!

Není to tím, že bych se na ní vykašlal. Místo zhoršování UX, jsem se rozhodl, že mi stačí jednoduchá analytika. Koukal jsem, že na Twitteru je to žhavé téma a nikdo neví pořádně jak na to. A když, tak na to plánuje prodávat školení nebo já nevím co. Přitom je to taková prkotina…

Základem je neřešit kód, co vám vygenerují GA, protože ten asi tak snadno nenakonfigurujete. A pořád vkládáte jejich script do stránky a to je to, co nechceme. Analytics mají své API. Můžeme si i rozšířit svou znalost webové platformy o zajímavé API pro asynchroní zasílání analytických dat navigator.sen­dBeacon:

<script>
   self._ga = {
     // Full Measurement Protocol param reference:
     // https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters
     data: {
       v: "1", // Measurement Protocol version.
       tid: "UA-TVUJ-TRACKING-KOD", // Tracking ID.
       cid: `${Date.now()}${Math.random()}`, // Client ID.
       dl: location.href, // Document location.
       aip: 1, // Anonymize IP
     },
     send(additionalParams) {
       navigator.sendBeacon(
         "https://google-analytics.com/collect",
         new URLSearchParams({
           ...this.data,
           ...additionalParams,
         }).toString()
       );
     },
   };
   _ga.send({ t: "pageview" });
 </script>

Nahraďte UA-TVUJ-TRACKING-KOD za kód, který vám analytics vygenerují třeba pro použití v GTM (nebo je někde v původním tracking code).

Zajímavým plusem tohoto řešení je, že vám ze statistik zmizí IE11 a starší, které Beacon API nemají. Takže ho pak můžete přestat s klidným srdcem řešit. A to se vyplatí!

Pokud používáte GA4, tak tenhle kód vám fungovat nebude. Ale taky má API! Těžko říct, zda je použitelné.

Kód jsem sám nevymýšlel, ale okopíroval jsem jej od bývalého zaměstnance Googlu – Surmy.

Užívejte!

Jednoduše a rychle: lit-html

Aleš Roubíček |

Přiznejme si to. Dnešní stav tvorby webových frontendů není jednoduchý a začít novou věc vyžaduje spoustu kroků, které dříve nebyly potřeba a považovalo se to za výhodu webu. Dneska musíte znát Reacty, Babely, TypeScripty, Webpacky, CRAppy a co já vím, abyste vůbec vytvořili Hello World. Nebo aspoň si to velká část Frontend vývojářů asi myslí, že je to dobrý nápad, a tak webu lámou nožičky.

Nejen, že je takový přístup drahý na vývoj i údržbu, ale hlavně je to nesmysl. Já vím, velké enterprise systémy mají jiné požadavky… Je pak fajn, když za námi chodí zákazníci s přesně takovými systémy, že by potřebovali něco malého, rychlého a snadno použitelného, protože u nich to nejde.

No, ale dneska se pojďme raději věnovat něčemu jinému – knihovně Lit 2.0, která vám umožní psát jako v Reactu, ale bez všech těch zbytečných kejklí okolo. Tato knihovna vznikla v Google. V teamu, který tvořil framework Polymer. Knihovna využívá vlastnosti moderní webové platformy. Neřeší tak dávno neexistující problémy jako například nástroje, které jsem už jmenoval. Díky tomu může být malá (~5 KiB gzip), rychlá a dá se snadno komponovat s dalšími knihovnami, abyste mohli tvořit systémy, které řeší vaše business problémy. Nikoliv problémy špatných komplikovaných frameworků a nástrojů.

Knihovna Lit se historicky skládá ze dvou částí – lit-html a LitElement. Dnes se budeme věnovat té první.

lit-html

Knihovna lit-html má na starosti renderování šablon. Představte si React, který má jen čisté funkce jako komponenty, a místo JSX, který vyžaduje kompilační krok, používá nativní feature ECMAScriptu 2015 – tagované šablony.

Jako ukázku užití si vezmeme dynamické vkládání nekritických stylů do stránky:

<script type="module">
    import {render, html} from "https://unpkg.com/lit-html@2.0.2/lit-html.js?module";

    render(html`<link rel="stylesheet" href="/assets/main.css">`, document.head);
</script>

První věc, které si můžete všimnout, je použití ESM (ECMAScript Modules) v HTML. Jakmile máte podporu ESM modulů v browseru (je tam už asi 3 roky), máte většinu moderních vlastností ECMAScriptu k dispozici a nemusíte řešit transpilaci nebo polyfilly. A tak můžeme směle importovat!

Využijeme službu unpkg.com, která slouží jako CDN pro distribuci npm balíčků s možností transformace node_modules resolution na unpkg URLs – k tomu slouží GET parametr module.

Z balíčku si vyzobneme funkci render a funkci html, která slouží k tagování textových literálů. Funkce render bere jako první parametr šablonu, jako druhý parametr, kam se má vyrenderovat, a třetí, nepovinný, parametr umožňuje přesněji specifikovat, jak se má vyrenderovaný kód vkládat.

Základní chování je takové, že se první render šablony připojí do cílového elementu na konec. Další volání render už tento kus přepisuje. Tohle je jediná breaking change oproti lit-html 1.0, které vždy obsah elementu přepisovalo.

Tahat skoro 3 KiB závislostí jenom kvůli hezkému vložení stylopisu je samozřejmě nesmysl, ale je to pěkná ukázka mnoha zajímavých vlastností. Sami si to můžete hned vyzkoušet ve svém browseru, stačí vám jen DevTools, které tam už jsou. Zápis šablon je úplně přirozený. Nemusíte se učit žádnou novou speciální syntaxi, kterou byste museli kompilovat, stačí vám HTML a ECMAScript. Tak, jak v browserech jsou už roky.

Všimnout si můžete také toho, že funkci render nemusíte předávat žádnou komponentu, ale prostě kus šablony. Přidávat abstrakce můžete, až když je to potřeba. Jestli vůbec taková situace nastane. Stejně tak můžete začít s inline scripty ve stránkách a extrahovat je do samostatných souborů, až když potřebujete sdílet netriviální množství kódu mezi různými stránkami. Já třeba takto dělal web a PWA Hlídače shopů.

Šablonování

Tagované šablony umí interpolaci výrazů a proměnných, což nám umožňuje vytvářet opravdové šablony, do kterých se vkládají data.

export function resultsEmbed(url) {
  const parameters = new URLSearchParams({ url, view: "embed" });
  return html`
    <iframe
      sandbox="allow-same-origin allow-scripts allow-top-navigation allow-popups"
      class="hs-result__embed"
      src="/app/?${parameters}"
    ></iframe>
  `;
}

Tady třeba generujeme iframe s výsledky Hlídače. Pro naformátování GET parametrů použijeme užitečnou třídu URLSearchParams, která se postará o správné encodování hodnot parametrů. Ano, není to tak chytré jako Latte, které pozná samo, jak má vstupy encodovat, ale pořád máme po ruce všechna potřebná primitiva jazyka/platformy. Bez komplikací, bez magie.

Zajímavostí je, že v předchozí verzi 1.0 se u parameters muselo explicitně zavolat toString()/valueOf(). Ve verzi 2.0 se to chová, jak byste u interpolace proměnných v šabloně čekali.

Direktivy

Lit používá pro obohacení chování šablon speciální funkce – direktivy. Například, pokud používáte data, která implementují rozhraní Iterable, nemusíte nad nimi volat Array.from, abyste mohli použít metodu map pro mapování jednotlivých prvků. Můžete místo toho použít direktivu repeat:

export function shopsListTemplate(shops) {
  return html`
    <ul class="hs-shops-list">
      ${repeat(
        shops,
        ({ name, url }) => html`
          <li class="hs-shops-list__item">
            <a href="${url}">${name}</a>
          </li>
        `
      )}
    </ul>
  `;
}

V knihovně je zhruba tucet užitečných direktiv a zároveň základní třída pro implementaci vlastních direktiv.

Závěrem

Lit-html 2.0 je jednoduchá a malá knihovna, která nám umožňuje šablonovat HTML pomocí čistého ECMAScriptu bez potřeby jakýchkoliv kompilátorů. Zkrátka, otevřete browser, DevTools napojíte na lokální HTML soubor a můžete vyvíjet živou aplikaci (REPL style). Já ji používám jako náhradu jak jQuery, tak Reactu. Problémy, které ve své době řešili, jsou už pryč a není třeba s nimi uživatele mučit.

Pokud chcete z vašich šablon udělat znovupoužitelné komponenty, můžete použít další část knihovny Lit – LitElement, na který se podíváme někdy příště. Prozradím jen, že jde o základní třídu pro psaní Web Components, která využívá lit-html pro šablonování a zjednodušuje API, které je podobné základní třídě React.Component. Jen je to malé (zhruba jako Preact), jednoduché a rychlé.

Pulumi a esbuild

Aleš Roubíček |

Poslední tři měsíce jsem z velké části, stejně jako minulý rok, pracoval na Hlídači Shopů. Hodně úsilí jsme věnoval splácení technického dluhu, abychom mohli Hlídače snáze a rychleji (tj. levněji) rozšiřovat. Vytvořil jsem integrační a deployment pipeline, včetně popisu většiny infrastruktury pomocí IaC nástroje Pulumi. Dalším cílem bylo sjednocení code base extenze a webu, aby se daly snadno sdílet části logiky.

V první fázi bylo důležité mít co nejrychleji zdokumentovanou infrastrukturu a být schopní ji automatizovaně rozvíjet. Proto jsem zvolil integrovaný přístup, kdy aplikační a infrastrukturní kód jsou jedna code base, a Pulumi, dík své chytristice, rozhodne, kde se kód řízne a co se nasadí. Takže to byl takový TypeScriptový (dále TS) monolit.

Už během migrace původních lambdiček do Pulumi jsem narážel na takové to svědění, způsobené typovým sebeklamem. Často jsem musel uspokojovat kompilátor, kvůli jeho neschopné typové inferenci, nebo vyloženému ničení explicitně deklarovaných typů. Děkuju pěkně, ale mám lepší představy, jak bych mohl trávit svůj čas. Když pominu, že mám během kompilace TS víc času na čtení twitteru, tak jeho jedinou přidanou hodnotou je popis schémat, na který je vyloženě špatným nástrojem…

AWS vydalo novou verzi JS SDK – verzi třetí. Ta si klade za cíl modularizovat všeobjímající God knihovnu aws-sdk, na sadu specializovaných balíčků, které reflektují stav moderního JavaScriptu, takže obsahují plnohodnotné ESM s moderní syntaxí, ne ty hybridní blbiny pro webpacky v ES5, s kterýma se nedá nic jiného dělat, než nad tou hromádkou neštěstí brečet. Ano, nové AWS SDK obsahuje i plnohodnotné (ehm) TS soubory, protože je v nich napsáno. V praxi se však ukázalo, že je to cesta do pekla…

AWS oznámilo podporu Docker images pro nasazování AWS Lambda funkcí. Chvíli jsem uvažoval, že využiju schopnosti Dockeru mít popis build i runtime kontejnerů v jednom souboru a že každá lambda si tak snadno připraví (pomocí tsc) svoje výstupy a ty se pak nasadí do runtime jako jeden bundle…

O tom, že esbuild je pekelně rychlý a jednoduchý nástroj na kompilaci moderního JavaScriptu (aka ECMAScript 2020), jsem psal minule. Mimochodem umí stejně rychle konzumovat JSX, TS a TSX. Navíc má pěkné API použitelné i z JavaScriptu!

Pulumi má virtualizované balíčkování kódu pro deployment AWS Lambda funkcí. Krom toho, že si můžete nechat transparentně serializovat část kódu v podobě funkce, můžete si také nechat zabalit třeba adresář s kódem, který chcete nasadit. Ale ten už musí být nějak předpřipravený. V rámci virtualizace, ale dojdete až na úroveň, kde do archívu můžete strkat jakýkoliv string, nebo jeho Promise. To je krása opravdových abstrakcí…

JS API esbuildu umí, když mu nastavíte write: false, vracet výsledek bundlování jako, ta-dá, Promise of string. Takže, když si spustíme build service – kvůli optimalizacím na sdílený kód – a při vytváření popisu infrastruktury si necháme také připravit bundle dané funkce, můžeme ho rovnou nasadit. No, a protože to je esbuild a ne kombinace tsc a pulumi, tak je to rychlý nejen v build time, ale i v runtime. A to už se vyplatí!

Více o dopadech na latence jsem sepsal do vlákna na Twitteru, kde najdete i patřičné grafy.

PS: Na zrychlení má podíl nejen menší bundle, ale i nové AWS SDK, které k tomu zmenšení také přispívá, ale asi dělá i něco míň špatně.

Jednoduše a rychle: esbuild

Aleš Roubíček |

Znáte to. Chcete začít nový projekt. A. To. Rychle. Takže sáhnete po něčem, co vám tu dřinu výběru vhodných nástrojů usnadní. Typicky CRApp nebo něco podobnýho, co vám nejen vytvoří (většinou nevhodnou) strukturu projektu, ale i nasype tunu nepotřebných závislostí, které nemúžete nijak ovlivnit. Ale život je skvělý a vy můžete směle budovat!

A tak budujete. Občas zakopnete o nějaký ten klacek, který vám CRApp ze cviku hodí pod nohy. Občas si počkáte 20 sekund na rebuild jednoho změněného řádku. Ale co, vám to nevadí. Vy jste ostřílení bojem webového vývoje a tohle je přece o sto honů lepší něž ty dřevní doby s jQuery. Nebo tomu aspoň chcete věřit. Minimálně do chvíle než jdete do produkce a nahodou si spustíte Lighthouse.

„Ten se asi posral,“ říkáte si, když vám za performance udělil celých 16 bodů ze sta. No a co? Máchal ukazoval na TopMonks Caffè, že ani 15 bodů nemusí být nutně zle. Ha! Takže jste vlastně na tom strašně dobře! V představách se poplácáme po ramenou a šup s tím k uživatelům! Ti jsou celý nadšení, že jim tlačíte 4 MB kódu a mají čas si zatím udělat čaj, než se jim to načte a spustí.

Pak si začnou stěžovat a vy začnete laborovat, co změnit, aby se to zlepšilo? Dokud neuděláte eject, tak neuděláte nic. CRApp je by design, ochrana nezletilých, aby neudělali nic špatně. V důsledku, máte zapečené postupy, které byly platné před 5 lety a nic s tím neuděláte. Jenže web se za tu dobu posunul. Hodně posunul.

esbuild

Ušetřete si spousu práce s ejectem, protože i s ním se dostanete do bahna konfigurace Webpacku a Babelu, před kterým vás měl CRApp ušetřit, abyste se nezbláznili. Takže, rovnou odeberte všechny závislosti na CRApp, a místo nich si přidejte esbuild. Konfigurace je minimální, vlastně žádnou ani nepotřebujete.

esbuild src/index.js --bundle --output public/index.js

A máte hotovo. Fakt. Takhle rychle.

Nejprve vám přijde, že to asi nefunguje, protože proces se hned ukončil, aniž by viditelně cokoliv dělal. Pro jistotu se jdete podívat do publicu. Výstupy tam jsou…

Tak ho radši smažete, protože to tam mohlo být ještě od Webpacku. Spustíte znova a stejný efekt…

Ano. Takhle rychlý může být váš tooling, když neděláte zjevné píčoviny. Zvyká se na to snadno. Dopřejte si trochu lásky a nepoužívejte špatné, ale opravdu špatné, nástroje.

Rychlé a jednoduché nástroje vám umožní funguvat jak potřebujete. Já jsem si třeba pro hlídání změn a rychlý rebuild, napsal jednoduchý script, který vytvoří esbuild server, a pomocí chockidaru hlídá změny na filesystému. Můžete si tam snadno zapojit další užitečné nástroje.

Někdy příště se podíváme, jak na tu performance na výstupu.