Web Components
can do that ?!
Why
Web Components ?
a very big company
🖥 Many applications
🛠 Many technologies
👥 Many teams
Need consistency
Need interoperability
Need stability
In big companies
At an UI level
A Design system
👇🏼
In big companies
At a technical level
Web components
👇🏼
Framework
Web components
orchestrate
plays
Agenda
2.
Use cases
3.
What is next ?
1.
The 4 trends for Web Components
Vincent Ogloblinsky
Frontend software architect
The
4 trends
for
Web Components
Librairies
Compilers
Backporters
Framework
Librairies
2 approaches :
class based
functional based
Librairies - Class based
import {
LitElement,
html,
property,
customElement
} from 'lit-element';
@customElement('simple-greeting')
export class SimpleGreeting extends LitElement {
@property() name = 'World';
render() {
return html`<p>Hello, ${this.name}!</p>`;
}
}
<simple-greeting name="Everyone"></simple-greeting>
Librairies - Class based
import {Slim} from 'slim-js'
import {
tag,
template,
attribute
} from 'slim-js/Decorators'
@tag('simple-greeting')
@template('<p>Hello, {{name}}!</p>')
class MyTag extends Slim {
@attribute
name
}
<simple-greeting name="Everyone"></simple-greeting>
Librairies - Functional based
import
Element, { html }
from '@skatejs/element-lit-html';
export default class extends Element {
static get props() {
return {
name: String
};
}
render() {
return html`
<p>Hello, ${this.name}!</p>
`;
}
}
<simple-greeting name="Everyone"></simple-greeting>
Librairies - Functional based
import { html, define } from 'hybrids';
export const HelloWorld = {
name: '',
render: ({ name }) => html`
<p>Hello, ${name}</p>
`,
};
define('simple-greeting', HelloWorld);
<simple-greeting name="Everyone"></simple-greeting>
Framework
import { define, WeElement, html, render } from 'omi'
define('simple-greeting', class extends WeElement {
render(props) {
return html`<p>{props.text}</p>`
}
});
<simple-greeting name="Everyone"></simple-greeting>
Compilers
<!-- simple-greeting.riot -->
<p>{ props.message }</p>
import { register, mount, component } from 'riot';
import simpleGreeting from './simple-greeting.riot';
// register the my-component as global component
register('simple-greeting', simpleGreeting);
// find all the DOM nodes called `<simple-greeting>` and
// mount them with the component previously registered
mount('simple-greeting');
<simple-greeting name="Everyone"></simple-greeting>
Compilers
<p>{name}</p>
<script>
export default {
tag: 'simple-greeting',
props: ['name']
}
</script>
/**
* -> Compile it with Svelte CLI
*/
<simple-greeting name="Everyone"></simple-greeting>
Compilers
import { h, Component, Prop } from '@stencil/core';
@Component({
tag: 'simple-greeting'
})
export class SimpleGreeting {
@Prop() name: string;
render() {
return <p>{this.name}</p>;
}
}
<simple-greeting name="Everyone"></simple-greeting>
Backporters
import { Component, Input } from '@angular/core';
@Component({
selector: 'simple-greeting',
template: `
<p>{{ name }}</p>
`
})
export class SimpleGreeting {
@Input() name: string;
}
<simple-greeting name="Everyone"></simple-greeting>
import { ɵrenderComponent } from '@angular/core';
import { SimpleGreeting } from './simple-greeting.component';
export class SimpleGreetingElement extends HTMLElement {
private comp: SimpleGreeting;
constructor() {
super();
this.comp = ɵrenderComponent(SimpleGreeting, { host: this });
}
}
Backporters
<template>
<p>{name}</p>
</template>
<script>
export default {
name: 'SimpleGreeting',
props: ['name']
}
</script>
/**
* -> Compile it with Vue CLI
*/
<simple-greeting name="Everyone"></simple-greeting>
Backporters
import { WidgetBase } from '@dojo/framework/widget-core/WidgetBase';
import customElement from '@dojo/framework/widget-core/decorators/customElement';
import { v } from '@dojo/framework/widget-core/d';
import { ThemedMixin } from '@dojo/framework/widget-core/mixins/Themed';
@customElement({
tag: 'simple-greeting',
events: [],
attributes: ['name'],
properties: []
})
export default class SimpleGreeting extends ThemedMixin(WidgetBase) {
protected render() {
const { name } = this.properties;
return v('s', {}, [name]);
}
}
/**
* -> Compile it with Dojo CLI
*/
<simple-greeting name="Everyone"></simple-greeting>
Backporters
Benchmark
Make a choice
Warning !
Take care of Developer eXperience (DX)
versus
User eXperience (UX)
Use-cases
1 - Design System
Centralized library of components that can be shared across teams and projects to simplify design and development while ensuring consistent brand experiences, at scale.
Design System
Design System - Advantages
📉 Design debt
📉🤜🏻🤛🏻 Collaboration and communication problems
📉 Repetitive work
Consistency within a product family
Design System - Examples
Design System - How to ?
Balance consistency with flexibility
Component describes visual language & functional behavior
Journey which designers and developers take together
Design System - How to ?
Code with Web Components
...
Design System - How to ?
Tooling with open-wc
Design System - How to ?
Documentation with Storybook
Design System - How to ?
Share them with
Bit or npm
2 - Standalone widget
3 - Framework migration
Most of the time
⛪️ Catholic wedding with framework
🏗 Architecture is based on a framework
Framework migration
?
?
?
?
Framework migration
2 strategies :
💣 Big bang
🏗 Incremental
Framework migration
WC
WC
WC
WC
Framework migration
Web component integration
Migrate existing component to WC
Check your framework's WC support
Framework migration
Check framework WC support
Framework migration
Feedbacks from companies
Warning on internal UI libraries and compatibility
Transition time between old & new screen
Stop coupling a lot
It just "works"
4 - Micro frontends
The idea behind Micro Frontends is to think about a website or web app as a composition of features which are owned by independent teams. Each team has a distinct area of business or mission it cares about and specialises in. A team is cross functional and develops its features end-to-end, from database to user interface. (micro-fontend.org)
Micro frontends
Micro frontends
Strategies
Fragment concept (Zalando) - Project Mosaic
Single-spa - Framework which connects frameworks with iframes
Web Components
Micro frontends
Starting questions
How big is your application in terms of team and code ?
Can you divide your app into small pieces by domain accessory ?
How easy can you release small features for your application ?
What's next ?
Move from static to dynamic template, also during update
<template id="person">
<section>
<h1>{{name}}</h1>
Email: <a href="mailto:{{email}}">{{email}}</a>
</section>
</template>
let template = document.querySelector('#person');
let instance = template.createInstance({
name: "Ryosuke Niwa",
email: "rniwa@webkit.org"
});
CSS Shadow parts
Style elements inside Shadow DOM
<x-foo>
#shadow-root
<div part="some-box"><span>...</span></div>
<input part="some-input">
<div>...</div> /_ not styleable _/
</x-foo>
x-foo::part(some-box) { ... }
Chrome 74
Create stylesheet objects from script & remove the need for declarative style elements
const myElementSheet = new CSSStyleSheet();
class MyElement extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: "open" });
shadowRoot.adoptedStyleSheets = [myElementSheet];
}
connectedCallback() {
if (myElementSheet.cssRules.length == 0) {
myElementSheet.replaceSync(styleText);
}
}
}
ECMAScript 6 native module for HTML & CSS
<script type="module">
import {content} from "import.html"
document.body.appendChild(content);
</script>
<div id="blogPost">
<p>Content...</p>
</div>
<script type="module">
let blogPost = import.meta.document.querySelector("#blogPost");
export {blogPost}
</script>
ECMAScript 6 native module for HTML & CSS
import {content} from "import.html"
import styles from './styles.css';
class MyElement extends HTMLElement {
constructor() {
this.attachShadow({mode: open});
// push() doesn't actually exist yet
this.shadowRoot.moreStyleSheets.push(styles);
this.shadowRoot.appendChild(content);
}
}
Chrome 73
Namespacing for Custom Element definition
// Create a new registry that inherits from the global registry
const myRegistry = new CustomElementRegistry(window.customElements);
// Use the local registry when creating the ShadowRoot
element.attachShadow({mode: 'open', customElements: myRegistry});
// Define a trivial subclass of XFoo so that we can register it ourselves
class MyFoo extends XFoo {}
// Register it as `my-foo` locally.
myRegistry.define('my-foo', MyFoo);
// the definiton of my-foo will be resolved from the custom registry
const myFoo = this.shadowRoot.createElement('my-foo');
element.shadowRoot.appendChild(myFoo);
Conclusion
Analyze your application(s)
Start small, mix 1 by 1
Choose the correct scope of your WC(s)
Take care of performance issues
Automate everything
Resources
9 Web Components UI Libraries You Should Know in 2019
https://blog.bitsrc.io/9-web-component-ui-libraries-you-should-know-in-2019-9d4476c3f103
Stencil for Production design systems
Constructable Stylesheets - Chrome 73
https://developers.google.com/web/updates/2019/02/constructable-stylesheets
Resources
Design Systems Should be JavaScript Framework Agnostic
https://hackernoon.com/design-systems-should-be-javascript-framework-agnostic-2a0c47129ec8
UI in Microservices World — Micro Frontends pattern and Web Components
Keep It Simple Stupid
#UseThePlatform
Thanks for listening !
Ask me anything
Slides : bit.ly/wc-can-do-that
Web Components can do that ?
By Vincent Ogloblinsky
Web Components can do that ?
- 3,912