The most basic integration of
@porsche-design-system/components-js without any bundler after loading the npm package on any html page looks like
this.
<script> porscheDesignSystem.load();
</script>
Executing the load() function loads the core chunk from the CDN. The core chunk can easily be identified in the
network tab of Chrome Developer Tools by its name containing the version number, e.g.
porsche-design-system.v3.8.0.688f651c1314ab84fa7b.js.
Once the core chunk is loaded and executed the following things happen:
customElements.define() is called for every component to register the custom html element in the browser (this is
how prefixing for micro frontend architectures is even possible)if any of the registered component tags is already in or is later added to the DOM, its corresponding component
chunk is loaded from the CDN on demanda checklist is maintained that tracks which component chunks are loaded already
Now the Porsche Design System is active and ready to go.
In the example from above we didn't render any component, yet, so let's add a single p-button tag to the DOM.
<p-button>Hello</p-button>
For now, this is just a "dead" html tag without any appearance or functionality. You can't see it because it is hidden
via CSS by the mandatory getInitialStyles() partial which outputs something like
The core chunk detects the newly added tag and knows from its checklist that this component was not used and loaded
before. So it loads the component chunk, e.g. porsche-design-system.button.d471231621db4170e79a.js.
Once the component chunk is loaded, the component gets initialized.
Initialization of a component tag means that under the hood, an instance of the Button class is created in the
JavaScript space.
Important
It is important to understand, that for each component tag in the DOM, there is a component class instance in the JavaScript space.
The component tag acts as an interface, similar to an API, where you set attributes/properties or children as inputs.
If these change, the class instance detects these and renders the result into the component tag's Shadow DOM as an output.
Once the class instance is constructed by executing its constructor() the following lifecycle methods are executed in
order:
Different lifecycles can be used for validation, child or parent synchronisation, de-/registering event listeners,
layout calculations and many other things. But most importantly to render the actual component itself. Additional
information can be found in the official documentation for
Stencil Component Lifecycle Methods.
Once this process is complete, the hyrated CSS class gets added to the component tag.
<p-button class="hydrated">Hello</p-button>
Which makes the button appear like you are used to.Hello
This means that upon attribute or property change (sometimes also added/removed children for more interactive
components) the web component re-renders and updates its Shadow DOM.
Now that it is clear what is happening under the hood when a simple p-button is added to the DOM, let's see how this
looks from the perspective of network requests and how to improve them if necessary.
Status Quo
By default, the network traffic looks something like this.
A classic waterfall like loading behavior that
starts with the index.htmlthen continues with the index.js of @porsche-design-system/components-jswhich then loads the core chunkthat injects both the font-face.css and the component chunkand last the font file after the styles within the component's Shadow DOM are applied
As we can see, this happens in parallel with the index.js file.
Preloading font files
By applying the getFontLinks() partial we can preload the font assets. As a default, both
regular and semi-bold weights are preloaded since they are most commonly used but this can be customized.
As a result, both font files are additionally loaded in parallel, while earlier this happened not only in sequence but
even last and only when a style is present on the page that uses the font-family and that particular font-weight
which can lead to a phenomena called Flash of Unstyled Text (FOUT).
Preloading component chunks
The loading experience can be improved further by using the getComponentChunkLinks()
partial. Without any configuration it simply preloads the core chunk.
Again, with this improvement, the asset is now being loaded in parallel, too.
For the next step, we also want to preload the component chunk by using the partial like
Just preloading all component chunks on the page or even every chunk available should be avoided.
Instead, the most performant but also more complicated approach would be to only preload the component chunks
(and fonts) that are located above the fold which means visible on page load without scrolling.
Early initialization
There is one more improvement we can do, and that is to skip loading the index.js file of
@porsche-design-system/components-js. That can be achieved by using the getLoaderScript()
partial which essentially produces a script with the code necessary to load the core chunk and it also takes care
of calling porscheDesignSystem.load() so that the manual part from the initial setup is superfluous.
Therefore, the total amount of data transferred is basically the same but without the additional request and the
necessary http communication, like request and response headers. Also componentsReady()
isn't part of getLoaderScript().
Hint
This works and helps especially in a plain HTML and Vanilla Js setup since the index.js is otherwise
bundled by the JavaScript framework and you would end up shipping the same code twice, once bundled and once inlined
in the script tag.