Skip to content

Instantly share code, notes, and snippets.

@WebReflection
Last active January 8, 2024 07:16
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save WebReflection/ae3451c17c5e882bbc7f0714c14eefcd to your computer and use it in GitHub Desktop.
Save WebReflection/ae3451c17c5e882bbc7f0714c14eefcd to your computer and use it in GitHub Desktop.
A very simple comparison table between uce and lit-element.

A very simple comparison table between these two libraries.

uce lit-element
version 1.11.9 2.4.0
license ISC (simplified MIT) BSD-3-Clause License
language JS w/ TS definition TS w/ JS transpilation
size ( brotli ) 9437b ES5 / 6811b ES2015+ 8634b ES5 / 6708b ES2015+
polyfill ( brotli ) * 2.1k w/ builtin extends 5.2k CE w/out builtin or 31.7k w/ SD
minimal components size ** 73b plain 92b plain up to 30k+ plain
declaration object literal w/ extends the base class is LitElement only
extends-ability mixins classes
tooling-free import + @decorators
keyed & non-keyed ability
SSR ***
distributable components **** ⚠️
friendly syntax *****
builtin extends

* uce optional polyfill is @ungap/custom-elements which provides builtin extends for IE and Safari too, but no ShadowDOM, which can be optionally added via attachshadow (1.3k) or ShadyDOM (19.2k), while lit-element optional polyfill is @webcomponents/webcomponentsjs which provides Shadow DOM but, as I am writing today, no builtin extends.

** in uce the minimal custom element is customElements.whenDefined('uce-lib').then(({define})=>define('a-b',{})), while in lit-element it's import{LitElement}from'lit-element';customElements.define('a-b',class extends LitElement{}). Please read more if this part is not clear.

*** in uce the usage of ShadowDOM is not really necessary, neither encouraged, hence its components work out of the box even landed as SSR. See uce-template for an even richer SSR compatible experience. In lit-element, the usage of ShadowDOM is both encouraged and demoed all over, requiring its heavy polyfill if older browsers such as Edge, IE11, or legacy Mobile, are part of the targets, and yet, ShadowDOM is not SSR friendly, so choose carefully here.

**** in uce components can be distributed without carrying the library with them, thanks to its presence as customElements library, while lit-element components usually bundle the library in order to work, adding unnecessary bloat when components are published as stand-alone. That is: uce libarry size is O(1), while in lit-element it's currently O(n). Please read more if this part is not clear.

***** in uce interpolations can be just plain JS, no need to import, know, or learn, any repeater or specialized syntax/feature.


About the minimal component size

A Web page that uses uce can have this structure:

<!doctype html>
<html>
  <head>
    <script async src="/js/uce.js"></script>
    <script async src="js/components/component-a.js"></script>
    <script async src="js/components/component-b.js"></script>
    <script async src="js/components/component-c.js"></script>
  </head>
  <body>
    <component-a>content</component-a>
    <component-b>content</component-b>
    <component-c>content</component-c>
  </body>
</html>

All components can be developed without using any toolchain.

customElements.whenDefined('uce-lib').then(
  function (uce) {
    uce.define('component-a', {
      connected() {
        console.log('I am alive!');
      }
    });
  }
);

No import, no require, no external dependencies needed, the size of the library is completely irrelevant because it's exported through its own namespace defined as uce-lib Custom Element.

In lit-element, the structure of the page will be similar:

<!doctype html>
<html>
  <head>
    <script async src="js/components/component-a.js"></script>
    <script async src="js/components/component-b.js"></script>
    <script async src="js/components/component-c.js"></script>
  </head>
  <body>
    <component-a>content</component-a>
    <component-b>content</component-b>
    <component-c>content</component-c>
  </body>
</html>

However, accordingly with the way components are currently written, each of them requires either a toolchain, or an import, so that the library can be used.

import { LitElement } from 'lit-element';
customElements.define('component-a', class extends LitElement {
  connectedCallback() {
    console.log('I am alive');
  }
});

If a lit-element component is bundled, so that it can be deployed anywhere, the LitElement part of the library will be bundled too.

If a lit-element is not bundled, the import should have an absolute CDN url so that it can be deployed anywhere a third party CDN dependency is acceptable.

In this case, the minimal component size won't include the library itself but each component will require an import capable browser (no IE11, no old Edge), plus a network request, either to download once the library, or to grab it from the network cache.

However, most deployed components do include lit-element itself, until the day lit-element will use the same approach uce is using, so that components will be decoupled from the library itself, which can export all its namespace through the registerd Custom Element class.

Alternatively, lit-element could be pre-bundled and leak on the global window context, so that components can rely on its presence:

const { LitElement } = window.litElement;
customElements.define('component-a', class extends LitElement {
  // ...
});

However, this code would break if the global litElement namespace is not already available, while uce components will never break, even if they are evaluated before the uce library is included in the page.

@WebReflection
Copy link
Author

P.S. components written for uce will run without any change in uce-template too ... which is another benefit of not needing the library to define a component.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment