20

All the Ways to Make a Web Component - June 2020 Update

 3 years ago
source link: https://webcomponents.dev/blog/all-the-ways-to-make-a-web-component/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Photo by Pierre Bamin on Unsplash
Photo by Pierre Bamin on Unsplash

All the Ways to Make a Web Component - October 2020 Update

7th October, 2020
Compare coding style, bundle size and performance of 43 different ways to make a Web Component.

Introduction

The same <my-counter/> Web Component has been written in 43 variants.

Table of Content

Source Code

html5.svg

HTMLElement

Homepage: https://html.spec.whatwg.org/multipage/custom-elements.html

Try in WebComponents.dev
const template = document.createElement('template');
template.innerHTML = `
  <style>
    * {
      font-size: 200%;
    }

    span {
      width: 4rem;
      display: inline-block;
      text-align: center;
    }

    button {
      width: 4rem;
      height: 4rem;
      border: none;
      border-radius: 10px;
      background-color: seagreen;
      color: white;
    }
  </style>
  <button id="dec">-</button>
  <span id="count"></span>
  <button id="inc">+</button>`;

class MyCounter extends HTMLElement {
  constructor() {
    super();
    this.count = 0;
    this.attachShadow({ mode: 'open' });
  }

  connectedCallback() {
    this.shadowRoot.appendChild(template.content.cloneNode(true));
    this.shadowRoot.getElementById('inc').onclick = () => this.inc();
    this.shadowRoot.getElementById('dec').onclick = () => this.dec();
    this.update(this.count);
  }

  inc() {
    this.update(++this.count);
  }

  dec() {
    this.update(--this.count);
  }

  update(count) {
    this.shadowRoot.getElementById('count').innerHTML = count;
  }
}

customElements.define('my-counter', MyCounter);

Bundle Analysis

HTMLElement

No dependencies

Component size including library

Minified - Uncompressed993
Minified + Gzip505
Minified + Brotli363

Composition

Open Bundle Visualizer

Compilation details

All Sources have been Transpiled by WebComponents.dev compiler with the following Babel settings:

[
  [
    Babel.availablePresets['env'],
    {
      targets: {
        browsers:
          'last 2 Chrome versions, last 2 Firefox versions, last 2 Safari versions, last 2 iOS versions, last 2 Android versions',
      },
      modules: 'false',
      bugfixes: true,
    },
  ],
  [Babel.availablePresets['react']],
],
plugins: [
  [Babel.availablePlugins['proposal-decorators'], { legacy: true }],
  [Babel.availablePlugins['proposal-class-properties'], { loose: true }],
]

Bundling details

Bundled with Rollup

import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import { terser } from 'rollup-plugin-terser';

...

    input: 'source.js',
    output: {
      dir: 'dist'),
      format: 'esm',
    },
    plugins: [
      replace({
        'process.env.NODE_ENV': JSON.stringify('production'),
      }),
      withdeps ? resolve() : undefined,
      commonjs( /* { namedExports: for React } */ ),
      terser({ output: { comments: false } }),

...

Bundle Size Comparison

Bundle size of 1 component

If you only deliver a single <my-counter/> you have the full cost of the library for a single component. This is the total cost of a single component with the library included.

Notes

The bare HTMLElement component doesn't have any dependency and creates a unmatched tiny package.

Svelte is doing also very well with a very small library runtime cost primarily due to excellent tree-shaking. This also means that as the component use more of Svelte capabilities, more of the library will be included.

Angular 10, CanJS, React and Vue2 libraries go above the 20KB mark.

Vue3 "One Piece" is delivering on promises with 30% improvement versus version 2.

ElemX comes with MobX, it's powerful but it's not light.

The incoming LitElement v3 (branch next-major) 30% improvements, on par with preact

Some libraries bring significant value and are more feature rich than others. It's normal to see larger sizes in these cases.

Nevertheless, for a simple counter component, we would like to see more tree-shaking happening.

On the other hand, once you start having multiple components using a broad spectrum of the library capabilities, you end up with the entire library anyway.

Estimated Bundle size of 30 components using the same library

This is an estimated size of a bundle of 30 my-counter-like components using the same library. All components will share the library code so the estimated size is calculated with: 1 bundle-with-dependencies + 29x components-without-dependencies.

Notes

It's interesting to see that some libraries managed to be smaller than bare HTMLElement. This is achieved by sharing more runtime accross the different components. Individual components are smaller and pay for the runtime. A library is not always an overhead !

Again here, Vue3 "One Piece" is delivering smaller compiled components and smaller runtime than Vue2

Performance

Performance of Parsing JavaScript + Create DOM tree

Benchmarked with Tachometer on Chrome Version 86.0.4240.75.

The benchmark page is composed of 50 <my-counter></my-counter> laid out in a single page with the library bundle code inlined.

Notes

Take into consideration that there is a margin of error of 2ms.

Things that play a role in this performance figure are:

  • The size of the JavaScript to parse (more JavaScript = longer).
  • The performance of the library to create the DOM nodes from scratch.

Everything runs locally so the network download time is not taken into account here.

Everything runs on a relatively powerful laptop and results could be completely different on a lower-end device like a phone or a tablet.

Final Notes

It's hard to find so much diversity in a technical solution. It tells a lot about Web Components and the low level API provided. All the 43 variants could be on the same page if we wanted to!

On bundle size, the results are already very promising but there is still room for improvement:

  • Some libraries would benefit from splitting their features, to better benefit from tree-shaking.
  • Except Stencil and Svelte, none of the libraries offer CSS minification out of the box.

Web Components are well supported in browsers now. Still, for old browsers (including IE11), polyfills are required. The bundle sizes reported here are only for modern browsers that don't need any polyfills. It was too much additional work to include polyfills in this post. Maybe later... before they become obsolete.

We will report on new libraries and updates. Make sure to follow us on Twitter.

Feedback or Questions

Please chat with us on our Discrod or Twitter channels.

Thanks

Thanks to all those who helped us review and correct this analysis.

Changes since June release

  • Full upgrade of the build
  • Upgrade of all libraries including Angular 10 and Stencil 2
  • Improve the vanilla HTMLElement implementation
  • Added FAST element
  • Added Joist framework
  • Added ElemX
  • Added Mithril.js
  • Added Readymade
  • Added uce-template
  • Added AlpineJS
  • Added lit-html v2 (next-major branch)
  • Added lit-element v3 (next-major branch)
  • Added Vue 3 "One Piece" with a Minimum Viable Web Component wrapper
  • Stencil compiler target to Type: dist-custom-elements-bundle

Brought to you byFollow us

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK