Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Serverless renderování Webových komponent

Serverless renderování Webových komponent

https://pages.dev/ - Cloudflare Pages
https://opennext.js.org/cloudflare - OpenNext - Next.js na Cloudflare Pages

https://webcomponents.org/ - Public registry webových komponent
https://front-end.social/@heydon/113225763140995122
https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements - Using custom elements na MDN

https://github.com/webreflection/linkedom/ Linkedom na Githubu

Prior art
https://webreflection.medium.com/linkedom-a-jsdom-alternative-53dd8f699311 - Linkedom intro
https://www.mayank.co/blog/server-side-custom-elements - Server-side custom elements

rarouš.weblog
https://www.rarous.net/weblog/2024/02/25/zobrazovani-webmentions-ve-strance Refactoring web components z Lit na no-deps
https://github.com/rarous/rarousnet/blob/trunk/www.rarous.net/src/esm/discogs.js - Ukázka Isomorfní Web komponenty v praxi
https://github.com/rarous/rarousnet/blob/trunk/functions/kolekce/vinyly.js - Ukázka použití Isomorfní Web komponenty v praxi v Cloudflare Pages Function

Použil jsem obrázky Joan Cornellà https://joancornella.net/en/
Obrázek Spa Day od Heydona si můžete pořídit jako slušivé tričko https://webbed-briefs.teemill.com/product/spa-day-tee/

Aleš Roubíček

October 09, 2024
Tweet

Resources

More Decks by Aleš Roubíček

Other Decks in Programming

Transcript

  1. Kdo jsem? • Weby dělám 25 let • Bloguju 20

    let (rarouš.weblog) • Zkušenosti z Atlas.cz, startupů i korporátů • 10 let vývojář v ASP.NET (WebForms, MVC, AJAX, WebServices…) • 15+ let praktikuju eXtreme Programming & DevOps (lokální a cloud infra) • 18 let vývoje FE v JS (ASP.NET Atlas, Prototype, MooTools, jQuery, Angular, React, ClojureScript, Lit, no-deps) • 8 let zkušeností se Serverless architekturou • 4 roky DJ na FrontKonu 🎧🎶 (počítám i meziročník na WebExpo)
  2. Kdo jsem? • Solutions Architect (Hacker Camp Donut, EC PRT,

    Hlídačshopů.cz, TMCloud) • Data Engineer (Apify, Keboola) • Performance Engineer • Intenzivní trénink od Chrise Wilsona (Mosaic, IE, WPF, Chrome) • Design Engineer • Community Builder
  3. /ˈhækə(r)/ a person who uses or writes computer programs with

    enthusiasm and skill /ˈstjuːdiəʊ/ a room where an artist works
  4. BFF - Backend for Frontend • Backend, který je ve

    správě FE teamu • Ušitý na míru daného frontendu • Bez zátěže kompatibility s čímkoli, co musí klasický BE obsluhovat • Rychlé úpravy v rámci vývojového cyklu bez blokací
  5. Cloud fl are Pages • Low-friction Serverless/Edge hosting webových projektů

    • Distribuce statických assetů pomocí CDN • Postavené nad Cloud fl are Workers • Cloud fl are Pages Functions se kompilují do Workeru • Assety se ukládají do KV • Přístup ke všemu co platforma Cloud fl are Workers nabízí (D1, R2, KV, Durable Objects, Queues…) • Podpora OpenNext v runtime https://opennext.js.org/cloud fl are
  6. Cloud fl are Pages Functions • Běží v Cloud fl

    are Workers • V8 Isolates • ServiceWorkers v CDN (standard Web APIs) • Filesystem based routing • /functions/api/hello-world.js -> /api/hello-world • /functions/users/[user].js -> /users/rarous • /functions/deep/[[catch-all]].js -> /deep/2024-10-09/frontkon
  7. Cloud fl are Pages Functions export async function onRequestGet(context) {


    return new Response("hello world");
 } 
 
 export async function onRequestPost({request}) {
 let formData = await request.formData();
 // TODO: …
 return new Response("ok");
 } https://developers.cloud fl are.com/pages/functions/api-reference/
  8. –Heydon Pickering “Web components are bad at doing the things

    libraries like React shouldn't and needn't be doing.” https://front-end.social/@heydon/113225763140995122
  9. Web Components • Sada na sobě nezávislých konceptů • Custom

    Elements • Způsob, jak k vlastní HTML značce přidělit prototyp • Templates • Efektivní způsob, jak mutovat DOM • Shadow DOM • Interní UI komponenty - zapouzdření chování a stylů
  10. Custom Elements class MyWebComponent extends HTMLElement {
 static observedAttributes =

    [];
 connectedCallback() {}
 disconnectedCallback() {}
 adoptedCallback() {}
 attributeChangedCallback(name, oldValue, newValue) {}
 } customElements.define(
 "my-web-component", 
 MyWebComponent
 ); https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements
  11. Templates • Efektivní znovupoužití HTML markupu • Fragmenty, Sloty •

    Deklarativní Shadow DOM s shadowrootmode atributem
  12. Templates <template id=item-template><li>… < /li> < /template>
 
 let itemTemplate

    = document.getElementById("item-template");
 let fragment = document.createDocumentFragment();
 for (let item of data) {
 let itemEl = itemTemplate.content.cloneNode(true);
 // render `item` to `itemEl`
 fragment.appendChild(itemEl);
 }
 listEl.replaceChildren(fragment);
  13. Templates <template id=item-template><li>… < /li> < /template>
 
 let itemTemplate

    = document.getElementById("item-template");
 let fragment = document.createDocumentFragment();
 for (let item of data) {
 let itemEl = itemTemplate.content.cloneNode(true);
 // render `item` to `itemEl`
 fragment.appendChild(itemEl);
 }
 listEl.replaceChildren(fragment);
  14. Templates <template id=item-template><li>… < /li> < /template>
 
 let itemTemplate

    = document.getElementById("item-template");
 let fragment = document.createDocumentFragment();
 for (let item of data) {
 let itemEl = itemTemplate.content.cloneNode(true);
 // render `item` to `itemEl`
 fragment.appendChild(itemEl);
 }
 listEl.replaceChildren(fragment);
  15. Proč dělat SSR? • Je to cool a všichni o

    tom mluví • Mám pomalou SPA a lidi dlouho čekaj na její načtení • Chci performance, jednoduše a rychle
  16. Výchozí stav - autoregistrace class MyWebComponent extends HTMLElement {
 connectedCallback()

    {}
 } customElements.define(
 "my-web-component", 
 MyWebComponent
 );
  17. Export komponenty, autoregistrace jen v kontextu browseru export class MyWebComponent

    extends HTMLElement {
 connectedCallback() {}
 } if (globalThis.window ?. customElements) {
 customElements.define(
 "my-web-component", 
 MyWebComponent
 );
 }
  18. Export komponenty, autoregistrace jen v kontextu browseru export class MyWebComponent

    extends HTMLElement {
 connectedCallback() {}
 } if (globalThis.window ?. customElements) {
 customElements.define(
 "my-web-component", 
 MyWebComponent
 );
 }
  19. Lexikální scope místo globálního export function defMyWebComponent({ HTMLElement }) {


    class MyWebComponent extends HTMLElement {
 connectedCallback() {}
 }
 return MyWebComponent;
 } if (globalThis.window ?. customElements) {
 let MyWebComponent = defMyWebComponent(window);
 customElements.define(
 "my-web-component", 
 MyWebComponent
 );
 }
  20. Registrační helper export function defMyWebComponent({ HTMLElement, customElements }) {
 class

    MyWebComponent extends HTMLElement {
 static register(tagName) {
 customElements.define(tagName, this);
 }
 connectedCallback() {}
 }
 return MyWebComponent;
 } if (globalThis.window ?. customElements) {
 defMyWebComponent(window).register("my-web-component");
 }
  21. Co potřebujeme? • Framework? • Compiler? • Headless Browser? •

    DOM!!! • Linkedom (Node.js, Deno, Workers…, light-weight) • JSDOM (Node.js only, heavy)
  22. Spojíme to dohromady import { parseHTML } from "linkedom/worker";
 import

    { defMyWebComponent } from " .. /my-web-component.js"; export async function onRequestGet({ request, env }) {
 let resp = await env.ASSETS.fetch(new URL(request.url));
 let html = await resp.text();
 let { document, window } = parseHTML(html);
 defMyWebComponent(window).register("my-web-component");
 return new Response(document.toString(), resp);
 }
  23. Načteme stránku z ASSETS import { parseHTML } from "linkedom/worker";


    import { defMyWebComponent } from " .. /my-web-component.js"; export async function onRequestGet({ request, env }) {
 let resp = await env.ASSETS.fetch(new URL(request.url));
 let html = await resp.text();
 let { document, window } = parseHTML(html);
 defMyWebComponent(window).register("my-web-component");
 return new Response(document.toString(), resp);
 }
  24. Vytvoříme DOM import { parseHTML } from "linkedom/worker";
 import {

    defMyWebComponent } from " .. /my-web-component.js"; export async function onRequestGet({ request, env }) {
 let resp = await env.ASSETS.fetch(new URL(request.url));
 let html = await resp.text();
 let { document, window } = parseHTML(html);
 defMyWebComponent(window).register("my-web-component");
 return new Response(document.toString(), resp);
 }
  25. Zaregistrujeme naší web komponentu import { parseHTML } from "linkedom/worker";


    import { defMyWebComponent } from " .. /my-web-component.js"; export async function onRequestGet({ request, env }) {
 let resp = await env.ASSETS.fetch(new URL(request.url));
 let html = await resp.text();
 let { document, window } = parseHTML(html);
 defMyWebComponent(window).register("my-web-component");
 return new Response(document.toString(), resp);
 }
  26. Vyrenderujeme výsledné HTML import { parseHTML } from "linkedom/worker";
 import

    { defMyWebComponent } from " .. /my-web-component.js"; export async function onRequestGet({ request, env }) {
 let resp = await env.ASSETS.fetch(new URL(request.url));
 let html = await resp.text();
 let { document, window } = parseHTML(html);
 defMyWebComponent(window).register("my-web-component");
 return new Response(document.toString(), resp);
 }
  27. Výsledný kód import { parseHTML } from "linkedom/worker";
 import {

    defMyWebComponent } from " .. /my-web-component.js"; export async function onRequestGet({ request, env }) {
 let resp = await env.ASSETS.fetch(new URL(request.url));
 let html = await resp.text();
 let { document, window } = parseHTML(html);
 defMyWebComponent(window).register("my-web-component");
 return new Response(document.toString(), resp);
 }