Slide 1

Slide 1 text

Serverless renderování Webových komponent @[email protected] #frontkon

Slide 2

Slide 2 text

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)

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

/ˈhækə(r)/ a person who uses or writes computer programs with enthusiasm and skill /ˈstjuːdiəʊ/ a room where an artist works

Slide 6

Slide 6 text

Průzkum sálu

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

view = f(data)

Slide 9

Slide 9 text

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í

Slide 10

Slide 10 text

Cloud fl are Pages https://pages.dev/

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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/

Slide 14

Slide 14 text

Web Components https://developer.mozilla.org/en-US/docs/Web/API/Web_components

Slide 15

Slide 15 text

–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

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

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ů

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

Templates • Efektivní znovupoužití HTML markupu • Fragmenty, Sloty • Deklarativní Shadow DOM s shadowrootmode atributem

Slide 20

Slide 20 text

Templates
  • … < /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);
  • Slide 21

    Slide 21 text

    Templates 

  • … < /li>
 < /template>
  • Slide 22

    Slide 22 text

    Templates
  • … < /li> < /template>
 
 let itemTemplate = document.getElementById("item-template");
  • Slide 23

    Slide 23 text

    Templates
  • … < /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);
  • Slide 24

    Slide 24 text

    Templates
  • … < /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);
  • Slide 25

    Slide 25 text

    Serverless Side Rendering of Web Components

    Slide 26

    Slide 26 text

    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

    Slide 27

    Slide 27 text

    No content

    Slide 28

    Slide 28 text

    No content

    Slide 29

    Slide 29 text

    Výchozí stav - autoregistrace class MyWebComponent extends HTMLElement {
 connectedCallback() {}
 } customElements.define(
 "my-web-component", 
 MyWebComponent
 );

    Slide 30

    Slide 30 text

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

    Slide 31

    Slide 31 text

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

    Slide 32

    Slide 32 text

    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
 );
 }

    Slide 33

    Slide 33 text

    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");
 }

    Slide 34

    Slide 34 text

    Co potřebujeme? • Framework? • Compiler? • Headless Browser? • DOM!!! • Linkedom (Node.js, Deno, Workers…, light-weight) • JSDOM (Node.js only, heavy)

    Slide 35

    Slide 35 text

    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);
 }

    Slide 36

    Slide 36 text

    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);
 }

    Slide 37

    Slide 37 text

    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);
 }

    Slide 38

    Slide 38 text

    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);
 }

    Slide 39

    Slide 39 text

    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);
 }

    Slide 40

    Slide 40 text

    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);
 }

    Slide 41

    Slide 41 text

    Prior art • https://webre fl ection.medium.com/linkedom-a- jsdom-alternative-53dd8f699311 • https://www.mayank.co/blog/server-side-custom- elements

    Slide 42

    Slide 42 text

    Kam dal? • https://www.rarous.net/weblog/

    Slide 43

    Slide 43 text

    No content

    Slide 44

    Slide 44 text

    Děkuji za pozornost