Small helper script to help loading web components and their dependencies
<html>
<head>
<link rel="preload" href="my-webcomponent.js" as="script"
data-es5="my-webcomponentES5.js" data-lib="defineES5Element.js">
<link rel="preload" href="my-other-webcomponent.js" as="script"
data-es5="my-other-webcomponentES5.js" data-lib="defineES5Element.js">
<script src="import-webcomponents.js" async></script>
</head>
<body>
<my-webcomponent></my-webcomponent><br/>
<my-other-webcomponent></my-other-webcomponent>
</body>
</html>
Will load my-[other-]webcomponent.js
for modern browsers without delay and will load my-[other-]webcomponentES5.js
for old browsers after loading the polyfills and defineES5Element.js
. Modern browsers (80%) waste zero time, old browsers may unnecessarily load and discard the modern version (they shouldn't understand <link rel="preload" ...
but IEE does) and will load their version after import-webcomponents.js
is loaded and executed and after the polyfills and dependency library are loaded.
Using web components today - end of 2018 - is a challenge, though a worthwhile one. The problem is: what a web component does and how it works varies wildly depending on browser capabilities. For 80% of the users web components are a slick modern technology that requires minimal overhead and just works (TM) out of the box. For the other 20% they are a bloated abomination of convoluted (transpiled) code, barely kept afloat by the most advanced set of polyfills the web has to offer.
The standard approach for dealing with this is to set up your web server to decide whether to serve beauty or abomination based on who asks. I used to recommend that, too. Though I never did it myself, and I guess most projects don't bother either. And failing that, the authors of said polyfills recommend to wrap the abomination in another abomination to make it at least work. Somehow.
This situation is hardly acceptable. For the sake of 20% of the users - those 20% who care least for modern technology - we sacrifice footprint, performance, and debuggability for the other 80%.
Thus I worked out an approach that will deliver slick beauty to the 80% without delay and keep the 20% afloat. Somehow. Turns out, this approach has little disadvantage over the server side approach. The server could make the experience of the 20% a bit faster at the expense of duplicating logic.
The reason is, that client side code has to decide anyway, whether or not to load the polyfills. So import-webcomponents
is a small addition to the standard polyfill loader.
<link rel="preload" ...>
is a modern technology that tells the browser to load the designated file since it will be required soon. import-webcomponents.js
looks for all <link rel="preload" data-es5>
. It evaluates the browser capabilities and either tells the browser to evaluate the modern version or load polyfills and old version.
Thus in order to use import-webcomponents.js
you need to prepare two builds.
- The modern build should just rollup your web component and then uglify it.
- The backported build should rollup your web component, then transpile and then finally uglify.
If you have big dependencies (like Polymer) and want to load several web components separately, you should exclude the dependency from rollup and build it separately. In your modern code you just leave the respective import statement. In addition you may want to add a <link rel="preload" href="my-dependency.js">
to the head (without data-es5
!). That way there will be no delays for loading the web component and only then figuring out the dependency is required.
Caveat: A few intermediate browser versions exist that support web components but do not support import
. For these my plan is to load the backported version and load custom-elements-es5-adapter.js. The latter is not yet implemented!
For your backported version import-webcomponents.js
supports loading such (backported!) dependency libraries before your web components.
In your <head>
put the <link rel="preload">
first and only then
<script src="import-webcomponents.js" async></script>
Otherwise if the script is cached it may be executed while the head is still loading and it would miss components!
For optimal page load performance I recommend loading import-webcomponents.js
async
. In that case you may want your web components to be hidden until defined!
import-webcomponents.js
does
document.querySelectorAll('link[rel="preload"][data-es5]');
in order to determine what to do. It supports the following attributes in the <link>
elements:
- href
This is automatically evaluated by the browser andimport-webcomponents.js
will use it to determine what to evaluate for modern browsers (i.e. it adds a<script>
tag to the<head>
with the respectivesrc
attribute) - data-es5
string that determines what to load for old browsers - data-lib
library to load beforedata-es5
; use ES6import
in your modern code to load dependencies (addpreload
s withoutdata-es5
for these for optimal performance on modern browsers)
Web Components Root: import-webcomponents
uses three ways to determine from where to load the polyfills:
- standard polyfill approach:
window.WebComponents.root
- look for the
data-wc-root
attribute on the<script>
tag that loadedimport-webcomponents
- fall back to
/node_modules/@webcomponents/webcomponentsjs/