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

Web Components: native Komponenten fürs Web – das Ende der SPA Frameworks?

Web Components: native Komponenten fürs Web – das Ende der SPA Frameworks?

Komponenten – ein Begriff der uns Entwicklern sehr geläufig ist, egal ob als Windows-, Desktop- oder Webentwickler. Durch Kapselung von UI-Definition und Code-behind erhalten wir modulare und wiederverwendbare Bausteine zum Zusammensetzen der Oberfläche unserer Anwendung. Während wir damals im Web mit jQueryUI erste Komponenten entwickeln konnten, bedienen wir uns heute moderner Frameworks wie Angular, React oder Vue.js. Doch mit den Webstandards für HTML-Templates, Shadow DOM und Custom Elements erhalten wir die Möglichkeit, ein natives Komponentenmodell im Browser zu nutzen, gänzlich ohne Frameworks: Es leben die Web Components! In dieser Session klärt Manuel Rauber, wie Web Components funktionieren, wo die Vorteile und Nachteile liegen und ob wir heutzutage schon in der Lage sind, Single Page Applications zu entwickeln, die gänzlich ohne Frameworks auskommen. Sind Sie auch schon auf die Antworten gespannt?

https://github.com/thinktecture/basta-spring-2020-web-components

Manuel Rauber

February 25, 2020
Tweet

More Decks by Manuel Rauber

Other Decks in Programming

Transcript

  1. Web Components
    Native Komponenten für’s Web - das Ende der SPA Frameworks?
    Manuel Rauber
    @ManuelRauber
    Consultant @ Thinktecture AG

    View Slide

  2. Special Day “Modern Business Applications”
    Thema Sprecher Datum, Uhrzeit Raum
    Pragmatische Microservices mit .NET Core 3 –
    mehr als nur gRPC
    Christian Weyer DI, 25. Februar 2020,
    10.30 bis 11.30
    Ballsaal 2
    Web Components: native Komponenten fürs Web –
    das Ende der SPA Frameworks?
    Manuel Rauber DI, 25. Februar 2020,
    11.45 bis 12.45
    Ballsaal 2
    Progressive Web Apps – die Grundlagen Sebastian Springer DI, 25. Februar 2020,
    14.15 bis 15.15
    Ballsaal 2
    Progressive Web-Apps mit Angular:
    Tipps für Fortgeschrittene
    Christian Liebel DI, 25. Februar 2020,
    15.30 bis 16.30
    Ballsaal 2
    Blazor: SPAs im Browser mit C# und WebAssembly Christian Weyer DI, 25. Februar 2020,
    17.15 bis 18.15
    Ballsaal 2

    View Slide

  3. Manuel Rauber
    Consultant @ Thinktecture AG
    [email protected]
    @manuelrauber
    https://manuel-rauber.com
    Microsoft MVP
    Who Am I?

    View Slide

  4. • The challenge
    • Web Components
    • What are they?
    • How to use them?
    • How to create them?
    • Take Care & a peek into the future
    • What about frameworks?!
    Agenda

    View Slide

  5. The challenge

    View Slide

  6. The challenge
    https://dribbble.com/shots/7169781-Homestay-Web-Design https://dribbble.com/shots/7164322-Food-delivery-Landing-page-design

    View Slide

  7. DRY

    View Slide

  8. Components
    Components
    Components
    Components

    View Slide

  9. View Slide

  10. !

    View Slide


  11. !


    !
    !

    !
    !
    • No semantic
    • Intransparent
    • Changes are difficult
    • Framework dependent

    View Slide




  12. !
    !

    !
    !
    !
    +!
    -!
    !
    • Global Scoping
    • Naming conflicts
    • Styling conflicts

    View Slide

  13. !


    !
    !

    !
    !
    • Semantic
    • Local Scoping
    • Configuration
    • Bundle Import

    View Slide

  14. Web Components

    View Slide

  15. • Web Components are not a standard, but a collection of 3 technologies
    • Custom Elements
    • HTML templates
    • Shadow DOM
    • (HTML Imports)
    • Bring a native component model to web instead of having to use frameworks
    Web Components - What are they?

    View Slide

  16. Can I use …?
    IE11 Edge Chrome Android Opera Safari iOS
    Custom
    Elements
    ❌ ✅ ✅ ✅ ✅ ⚠ ⚠
    Shadow
    DOM
    ❌ ✅ ✅ ✅ ✅ ⚠ ⚠
    HTML
    Templates
    ❌ ✅ ✅ ✅ ✅ ✅ ✅

    View Slide

  17. Custom Elements

    View Slide

  18. • Create your own HTML tags
    • Lifecycle model (“connected”, “disconnected”, “attribute changed”, “adopted”)
    • Reusable
    • Decouple behavior from the document
    Custom Elements
    !

    View Slide

  19. • ES6 Class
    • Inherit from HTMLElement
    or any other HTML element
    • Need to be defined in a
    CustomElementRegistry
    Custom Elements
    class MyRating extends HTMLElement {
    constructor() {
    super();
    console.log('component constructed');
    }
    connectedCallback() {
    console.log('component added to DOM');
    }
    adoptedCallback() {
    console.log('component was moved in DOM');
    }
    disconnectedCallback() {
    console.log('component removed from DOM');
    }
    attributeChangedCallback(name, oldVal, newVal) {
    console.log('a attribute has been changed');
    }
    }
    window.customElements.define('my-rating', MyRating);
    const myRating =
    document.createElement('my-rating');
    document.body.appendChild(myRating);
    !// this moves the node!
    otherElement.appendChild(myRating);
    otherElement.removeChild(myRating);

    View Slide

  20. • Static getter observedAttributes
    returns all attributes which
    should be observed
    • All those attributes will trigger
    attributeChangedCallback
    upon a change
    • Does not execute on property
    change!
    Custom Elements - Observed Attributes
    class MyRating extends HTMLElement {
    constructor() {
    super();
    }
    static get observedAttributes() {
    return [ 'value' ];
    }
    attributeChangedCallback(name, oldVal, newVal) {
    if (name !!=== 'value') {
    this.innerHTML = `Rating value ${newVal}`;
    }
    }
    }
    !

    View Slide

  21. HTML Templates

    View Slide

  22. • HTML tag:
    • Parsed, but not rendered
    • Instantiated via JavaScript
    HTML Templates

    My Image Card!

    Description!
    !
    const template = document.querySelector('template');
    const templateInstance = document.importNode(template.content, true);
    const img = templateInstance.querySelector('img');
    img.setAttribute('src', 'http:!//a-link.com/foo.png');
    document.body.appendChild(templateInstance);

    View Slide

  23. Shadow DOM

    View Slide

  24. • Encapsulates HTML and CSS
    • Isolated DOM: elements within Shadow DOM are not selectable from outside
    • e.g. document.querySelector() won’t return a result
    • Scoped CSS: CSS styles stay within Shadow DOM, no leakage, no bleed-in
    • Composition: declarative, markup-based API, put elements from outside in
    • Simplifies CSS: no conflicts with existing classes and IDs
    • Productivity: app consists of chunks of components instead of one big page
    Shadow DOM
    https://developers.google.com/web/fundamentals/web-components/shadowdom

    View Slide

  25. • Shadow host
    DOM node that the
    shadow DOM is attached to
    • Shadow tree
    DOM tree inside shadow DOM
    • Shadow boundary
    Place where the shadow DOM
    ends and the regular DOM begins (“Light DOM”)
    • Shadow root
    Root node of the shadow tree
    Shadow DOM
    document
    shadow
    host
    Document Tree
    Shadow Tree
    shadow
    root
    Shadow boundary

    View Slide

  26. • attachShadow() attaches a shadow DOM to any HTML element
    • open and closed mode
    Shadow DOM

    !

    !
    <br/>const shadowHost = document.querySelector('div');<br/>const shadowRoot = shadowHost.attachShadow({ mode: 'open' });<br/>shadowRoot.innerHTML = '<h1>Hello World!!</h1>';<br/>!
    !
    !

    View Slide

  27. • open allows access to the HTML element’s shadow DOM
    • closed does not allow access to the HTML element’s shadow DOM
    Shadow DOM
    !// !!...
    shadowHost.attachShadow({ mode: 'open' });
    document.querySelector('div').shadowRoot.querySelector('h1');
    !// !-> [HTMLHeadingElement]
    !// !!...
    shadowHost.attachShadow({ mode: 'closed '});
    document.querySelector('div').shadowRoot.querySelector('h1');
    !// !-> Can not read 'querySelector' of null

    View Slide

  28. • A (named) placeholder
    to fill with your own
    markup
    • Basically mixes together
    two DOM trees:
    the shadow & light tree
    Shadow DOM - Slots

    <br/>:host { border: 1px solid black; display: block; }<br/>!

    !
    With Default Content!
    !
    !
    ! !!!

    Unnamed Slot Content!
    !

    Unnamed Slot Content 2!
    Another Slot Content!
    !

    View Slide

  29. • Slots emit an event, whenever its content changes: slotchange
    • Get the slotted elements via assignedNodes()
    • https://drafts.csswg.org/css-scoping/#slotted-pseudo
    Shadow DOM - Slots
    const slot = this.shadowRoot.querySelector('slot');
    slot.addEventListener('slotchange', () !=> {
    const assignedNodes = slot.assignedNodes();
    !// Do something with assignedNodes
    });

    View Slide

  30. • Styling is done via -tags<br/>• All stylings are local and do not overwrite styles from other shadow DOMs<br/>• No other stylings will bleed into the shadow DOM<br/>Shadow DOM - Styling<br/><template><br/><style><br/>h1 {<br/>color: red;<br/>}<br/>!
    Hello World!!
    !
    const shadowRoot = document.querySelector('div')
    .attachShadow({ mode: 'open' });
    shadowRoot.innerHTML = 'Hello World!!' +
    'h1 { color: red; }!';

    View Slide

  31. • Inside shadow DOM
    • :host, :host(), :host-context(), ::slotted()
    • Outside shadow DOM
    • CSS Shadow Parts: ::part()
    • In Discussion: ::theme()
    • Deprecated: >>> (“shadow piercing”)
    Shadow DOM - CSS Selectors

    View Slide

  32. • :host: Selects the shadow host element
    • :host(): Selects the shadow host element only, if it has a certain class
    • :host-context(): Selects the shadow host
    element only, if the selector given as the
    function's parameter matches the shadow
    host's ancestor(s) in the place it sits inside
    the DOM hierarchy
    • "::slotted(): Selects a slotted element
    if it matches the selector
    Shadow DOM - CSS Selectors - Inside Shadow DOM
    document
    shadow
    host
    Document Tree
    Shadow Tree
    shadow
    root
    Shadow boundary
    !

    !
    !
    !
    !

    !::slotted(div) {} !
    !
    !

    View Slide

  33. Shadow DOM - CSS Selectors - Inside Shadow DOM

    <br/>:host {<br/>display: block;<br/>background-color: blue;<br/>}<br/>:host(.red) { background-color: red; }<br/>:host-context(main) { background-color: yellow; }<br/>!::slotted(article) { background-color: black; }<br/>!

    Before slot!
    Default Slot content!
    After slot!
    !
    !
    !
    !

    !
    !


    Article Content
    !
    !

    View Slide

  34. • CSS Shadow Parts (Working Draft): "::part()
    • Allows to selectively expose elements from the shadow tree to the
    outside page for styling purposes
    • https://www.w3.org/TR/css-shadow-parts-1/
    • CSS Shadow Parts (Unofficial Draft): "::theme()
    • Matches all given parts, no matter how deeply nested they are
    • http://tabatkins.github.io/specs/css-shadow-parts/#part-theme
    Shadow DOM - CSS Selectors - Outside Shadow DOM

    View Slide

  35. • Allows to selectively expose
    elements from the shadow tree to
    the outside page for styling
    purposes
    Shadow DOM - CSS Selectors - Outside Shadow DOM - ::part

    <br/>header {<br/>background-color: crimson;<br/>}<br/>!

    Style me!
    !
    !
    <br/>my-element.styled!::part(my-header) {<br/>background-color: black;<br/>color: white;<br/>}<br/>!
    !
    !

    View Slide

  36. • If a nested Web Component
    will expose an inner Web
    Component’s part, it has to
    use exportparts=“part1 part2 …”
    instead of part=“name”
    Shadow DOM - CSS Selectors - Outside Shadow DOM - ::part
    <br/>my-element!::part(textspan) { color: red; }<br/>!

    exportparts="innerspan textspan”>
    !
    !

    Innerspan!
    Textspan!
    !
    !

    View Slide

  37. Importing Web Components

    View Slide

  38. • ES6 Import
    • Via Code
    • Deprecated: HTML Import
    Importing Web Components

    !
    !// static import
    import './my-element.js';
    !// dynamic import
    import('./my-element.js')
    .then(!!...);
    !// or lazy-loading via
    const script = document.createElement('script');
    script.src = './my-element.js';
    document.body.appendChild(script);
    const myElement = document.createElement('my-element');
    document.body.appendChild(myElement);

    View Slide

  39. CustomElementRegistry

    View Slide

  40. • Window global registry for all custom elements
    • API
    • define(): defines a new element
    • get(): returns the constructor of an element (or undefined)
    • whenDefined(): returns a promise which is fulfilled, when the custom
    element is available
    • upgrade(): upgrades an element before it is connected to its shadow
    root
    CustomElementRegistry

    View Slide

  41. Take Care!
    A sneak peek into the future

    View Slide

  42. Versioning
    My library A
    My library B
    3rd party
    library
    1.0
    3rd party
    library
    2.0
    Application


    defines
    defines
    Uncaught DOMException: Failed to execute ‘define’
    on ‘CustomElementRegistry’: the name “my-web-
    component” has already been used with this
    registry.

    View Slide

  43. • Problem: Custom Element Registry is a window global object
    • Possible solution currently discussed by W3C
    • Scoped Custom Element Registry
    • https://github.com/w3c/webcomponents/issues/716
    • Current usable solutions
    • Version your HTML tags
    • The host application is the only one loading WCs, even 3rd-party, but this
    could break other Web Components expecting a certain version
    Versioning

    View Slide

  44. • If you wish to sync them, then you’ll need to sync them!
    • “Reflecting properties to attributes” or “Reflected DOM Attributes”
    • Helps to keep DOM representation in sync with its JavaScript state
    • Watch out! Properties can hold objects, numbers etc. Attributes only strings

    Attributes vs Properties
    input = document.createElement('input');
    console.log(input.getAttribute('value')); !// null
    input.value = 'one';
    console.log(input.getAttribute('value')); !// null
    input.setAttribute('value', 'two');
    console.log(input.value); !// one

    View Slide

  45. • Shadow DOM encapsulates the DOM only
    • JavaScript is still global scoped
    • Use common techniques for scoping JavaScript
    • May use the global scope to your advantage for app-wide configurations
    • But global polyfills can cause problems (e.g. if you load zone.js twice)
    JavaScript is still global scoped

    View Slide

  46. • Easy things may require more code
    • Attributes vs Properties
    • addEventListener etc.
    • Bigger component libraries need a strong foundation
    • Think of when and how to render your component
    • There is no virtual DOM like in React or Angular
    • You may need to think of partial DOM updates for bigger components
    Boilerplate & render loops

    View Slide

  47. • Forms and Web Components don’t play well yet
    • FormData is not populated
    • does not work
    • Form Participation API aka “Form Attached Controls” is discussion
    • https://github.com/w3c/webcomponents/issues/815
    • https://github.com/w3c/webcomponents/issues/814
    • https://github.com/github/image-crop-element/pull/15
    • https://github.com/axa-ch/patterns-library/issues/1258
    Form Participation API

    View Slide

  48. • Already good possibilities to create framework-independent components
    • Decide between view-only and data-controlling Web Components
    • Keep in mind, what is not working yet, but will come in the future
    • Leverage the shadow DOM to encapsulate your Web Components
    • Think of your Web Components API!
    Web Components - Summary

    View Slide

  49. What about frameworks?!

    View Slide

  50. • Web Components are an uprising good alternative to build frontends
    without frameworks involved
    • But! A lot of stuff is done easier with frameworks, e.g. Form Validation, State
    Handling, Data Management, CSS post processing
    • Why not wrap framework components into Web Components?
    • Angular: Angular Elements, @angular/elements
    • React: Wrap manually or use a community wrapper
    • Vue: Vue Web Components, @vue/web-components
    What about frameworks?!

    View Slide

  51. DEMO

    View Slide

  52. Conclusion

    View Slide

  53. • Web Components help in giving a semantic structure in apps
    • Improves maintainability and reusability
    • Take care for stuff not working yet (e.g. Form Participation API)
    • SPA Frameworks can help in building Web Components
    • … with the odd of having bigger bundles
    • Web Components will make you think more about your APIs!
    Web Components - Summary

    View Slide

  54. Web Components Article Series
    https://thinktecture.com/newsletter
    COMING SOON!

    View Slide

  55. And… now?
    • Try it out! Fiddle around with Web Components
    • Use them! Include first small Web Components into your application
    • Be prepared! Keep up to date with the development of Web Components
    • Slides & Repo: https://thinktecture.com/manuel-rauber
    • Contact: [email protected] - @manuelrauber

    View Slide