2. A B O U T M E
{
"name": "Ilia Idakiev",
"experience": [
“Google Developer Expert (GDE)“,
"Developer & Co-founder @ HILLGRAND",
"Lecturer in 'Advanced JS' @ Sofia University",
"Contractor / Consultant",
"Public / Private Courses”
],
"involvedIn": [
"Angular Sofia", "SofiaJS / BeerJS",
]
}
3. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
SEPARATION OF CONCERNS (SOC)
▸ Design principle for separating a computer program into distinct sections, such
that each section addresses a separate concern. (Modularity)
4. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
S.O.L.I.D PRINCIPLES OF OBJECT-ORIENTED PROGRAMMING
▸ Single Responsibility Principle
▸ Open / Close Principle
▸ Liskov Substitution Principle
▸ Interface Segregation Principle
▸ Dependency Inversion Principle
http://aspiringcraftsman.com/2011/12/08/solid-javascript-single-responsibility-principle/
5. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
WEB COMPONENTS
▸ Introduced by Alex Russell (Chrome team @ Google)
at Fronteers Conference 2011
▸ A set of features currently being added by the W3C to
the HTML and DOM specifications that allow the creation of
reusable widgets or components in web documents and web applications.
▸ The intention behind them is to bring component-based software
engineering to the World Wide Web.
6. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
WEB COMPONENTS FEATURES:
▸ HTML Templates - an HTML fragment is not rendered, but stored until it is
instantiated via JavaScript.
▸ Shadow DOM - Encapsulated DOM and styling, with composition.
▸ Custom Elements - APIs to define new HTML elements.
▸ HTML Imports - Declarative methods of importing HTML documents into other
documents. (Replaced by ES6 Imports).
8. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
DEFINE CUSTOM ELEMENT
(function () {
}());
Create an isolated scope
counter.js
9. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
(function () {
class Counter extends HTMLElement {
}
}());
DEFINE CUSTOM ELEMENT Create a new class that extends HTMLElement
counter.js
10. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
(function () {
class Counter extends HTMLElement {
}
customElements.define('hg-counter', Counter);
}());
DEFINE CUSTOM ELEMENT Register the new custom element.
counter.js
11. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
HTML TEMPLATES
▸ The <template> tag holds its content hidden from the client.
▸ Content inside a <template> tag will be parsed but not rendered.
▸ The content can be visible and rendered later by using JavaScript.
12. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
WAYS TO CREATE A TEMPLATE
<template id="template">
<h2>Hello World</h2>
</template>
const template =
document.createElement('template');
template.innerHTML =
'<h2>Hello World</h2>';
Using HTML Using JavaScript
13. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
CREATE TEMPLATE HELPER FUNCTION
function createTemplate(string) {
const template = document.createElement('template');
template.innerHTML = string;
return template;
}
utils.js
14. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
CREATE THE TEMPLATE
(function () {
const template = createTemplate('<div>Hello World<div>');
class Counter extends HTMLElement {
}
customElements.define('hg-counter', Counter);
}());
Use the create template helper function.
counter.js
15. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
SHADOW DOM
▸ Isolated DOM - The component's DOM is self-contained
(e.g. document.querySelector() won't return nodes in the component's shadow DOM).
▸ Scoped CSS - CSS defined inside shadow DOM is scoped to it. Style rules
don't leak out and page styles don't bleed in.
▸ Composition - done with the <slot> element.
(Slots are placeholders inside your component that users can fill with their own markup).
16. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
DEFINE CUSTOM ELEMENT
(function () {
const template = createTemplate('<div>Hello World<div>');
class Counter extends HTMLElement {
constructor() {
super();
}
}
customElements.define('hg-counter', Counter);
}());
counter.js
Utilise the class constructor.
17. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
ATTACH SHADOW DOM
(function () {
const template = createTemplate('<div>Hello World<div>');
class Counter extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
}
}
customElements.define('hg-counter', Counter);
}());
Attach the shadow DOM.
counter.js
18. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
(function () {
const template = createTemplate('<div>Hello World<div>');
class Counter extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.appendChild(template.content.cloneNode(true));
}
}
customElements.define('hg-counter', Counter);
}());
CREATE THE TEMPLATE Attach the template contents to the shadow root.
counter.js
19. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
USE OUR CUSTOM ELEMENT
<body>
<hg-counter></hg-counter>
<script src="./util.js"></script>
<script src="./counter.js"></script>
</body>
index.html
20. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
EXTEND OUR CUSTOM ELEMENT
index.html
(function () {
const template = createTemplate(`
<div name="value"></div>
<button data-type=“dec">-</button>
<button data-type="inc">+</button>
`);
class Counter extends HTMLElement {
constructor() {
super();
21. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
EXTEND OUR CUSTOM ELEMENT
index.html
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.appendChild(template.content.cloneNode(true));
shadowRoot.addEventListener('click', ({ target }) => {
const type = target.getAttribute('data-type');
if (type === 'dec') {
this.counter--;
} else if (type === 'inc') {
this.counter++;
}
});
22. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
UPDATING THE DOM
utils.js
function updateDOM(root, updates) {
updates.forEach(item => {
root.querySelectorAll(`[name=${item.name}]`).forEach(element =>
element.textContent = item.value
);
});
}
23. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
EXTEND OUR CUSTOM ELEMENT
index.html
class Counter extends HTMLElement {
set counter(value) {
this._counter = value;
}
get counter() {
return this._counter;
}
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.appendChild(template.content.cloneNode(true));
this.counter = 0;
27. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
CUSTOM ELEMENTS LIFECYCLE CALLBACKS
▸ connectedCallback - Invoked each time the custom element is appended into a
document-connected element. This will happen each time the node is moved, and
may happen before the element's contents have been fully parsed.
▸ disconnectedCallback - Invoked each time the custom element is disconnected
from the document's DOM.
▸ attributeChangedCallback - Invoked each time one of the custom element's
attributes is added, removed, or changed.
▸ adoptedCallback - Invoked each time the custom element is moved to a new
document.
28. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
CUSTOM COMPONENT ATTRIBUTES
index.html
class Counter extends HTMLElement {
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'value') {
this.counter = newValue;
}
}
constructor() {
Handle attribute changes
29. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
CUSTOM COMPONENT ATTRIBUTES
index.html
class Counter extends HTMLElement {
static get observedAttributes() {
return ['value'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'value') {
this.counter = newValue;
}
}
constructor() {
Define which attributes should be watched
32. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
WEB COMPONENT CSS
▸ :host - selects the shadow host of the shadow DOM
▸ :host() - match only if the selector given as the function's parameter matches
the shadow host.
▸ :host-context() - match 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() - represents any element that has been placed into a slot inside an
HTML template
35. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
CUSTOM EVENTS Listening for custom event
• CustomEvent {isTrusted: false, detail: {value: true}, type: "toggle", …}
const el = document.getElementById('my-custom-element');
el.addEventListener('toggle', e => {
console.log(e);
});
main.js
Console
36. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
FURTHER READING
▸ Extending different HTML Elements
(e.g. HTMLButton)
37. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
BENEFITS OF USING CUSTOM COMPONENTS
▸ Framework agnostic - Written in JavaScript and native to the browser.
▸ Simplifies CSS - Scoped DOM means you can use simple CSS selectors, more
generic id/class names, and not worry about naming conflicts.
• Productivity - Think of apps in chunks of DOM rather than one large (global) page.
▸ Productivity - Think of apps in chunks of DOM rather than one large (global)
page.
39. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
COSTS OF USING CUSTOM COMPONENTS
▸ Template Generation - manually construct the DOM for our templates
(No JSX features or Structural Directives)
▸ DOM Updates - manually track and handle changes to our DOM
(No Virtual DOM or Change Detection)
40. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
LIT HTML
▸ Library Developed by the Polymer Team @ GOOGLE
▸ Efficient - lit-html is extremely fast. It uses fast platform features like
HTML <template> elements with native cloning.
▸ Expressive - lit-html gives you the full power of JavaScript and functional programming
patterns.
▸ Extensible - Different dialects of templates can be created with additional features for setting
element properties, declarative event handlers and more.
▸ It can be used standalone for simple tasks, or combined with a framework or component model,
like Web Components, for a full-featured UI development platform.
▸ It has an awesome VSC extension for syntax highlighting and formatting.
83. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
LIT ELEMENT
▸ A simple base class for creating fast, lightweight web components
https://lit-element.polymer-project.org/
85. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
LIT HTML DIRECTIVES (1)
▸ Directives are functions that can customize how lit-html renders values.
Template authors can use directives in their templates like other functions
86. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
LIT HTML DIRECTIVES (2)
▸ The returned function is called each time the part is rendered. The part argument is
a Part object with an API for directly managing the dynamic DOM associated with
expressions. Each type of binding has its own specific Part object:
▸ NodePart for content bindings.
▸ AttributePart for standard attribute bindings.
▸ BooleanAttributePart for boolean attribute bindings.
▸ EventPart for event bindings.
▸ PropertyPart for property bindings.
87. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
LIT HTML DIRECTIVES (3)
▸ Each of these part types implement a common API:
▸ value. Holds the current value of the part.
▸ setValue. Sets the pending value of the part.
▸ commit. Writes the pending value to the DOM. In most cases this happens
automatically—this method is only required for advanced use cases, like
asynchronous directives.
91. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
LIT HTML UNTIL BUILT-IN DIRECTIVE
92. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
LIT HTML BUILT-IN DIRECTIVES
▸ asyncAppend and asyncReplace
▸ cache
▸ classMap
▸ ifDefined
▸ guard
▸ repeat
▸ styleMap
▸ unsafeHTML
▸ until
93. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
LIT-HTML & HTML/CSS
▸ Dynamic css class/id and property names
▸ Dynamic css property values
▸ Sharing styles and HTML between the isolated web components (mixins)
94. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
WEB COMPONENTS + LIT-HTML VS REACT + JSX
▸ React and JSX are not standard and JSX requires a compiler.
▸ React reconciliation is doing diff checking per node so there might be a lot of
unnecessary checks when re-rendering and with lit-html the tag functions
separate static from dynamic parts so checks are very fast.
▸ LIT-HTML is very small ~ 2.5K and Web Components are native.
95. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
LARGER FRAMEWORKS
▸ StencilJS - a simple library for generating Web Components and
progressive web apps (PWA).
(built by the Ionic Framework team for its next generation of performant mobile and desktop Web
Components)
▸ Polymer - library for creating web components.
(built by Google and used by YouTube, Netflix, Google Earth and others)
▸ SkateJS - library providing functional abstraction over web
components.
▸ Angular Elements
96. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
ADDITIONAL RESOURCES
▸ https://custom-elements-everywhere.com - This project runs a suite of tests
against each framework to identify interoperability issues, and highlight
potential fixes already implemented in other frameworks.
97. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
WEB COMPONENTS SERVER SIDE RENDERING
▸ SkateJS SSR - @skatejs/ssr is a web component server-side
rendering and testing library. (uses undom)
▸ Rendertron - Rendertron is a headless Chrome rendering solution
designed to render & serialise web pages on the fly.
▸ Domino - Server-side DOM implementation based on Mozilla's
dom.js
98. CREATING LIGHTWEIGHT JS APPS W/ WEB COMPONENTS AND LIT-HTML
CONNECT
GitHub > https://github.com/iliaidakiev (/slides/ - list of future and past events)
Twitter > @ilia_idakiev