Component specific client-library in ui.frontend project and better dependency management for client libraries.
Use-case:
1. Currently, all the code written in ui.frontend will be bundled as 2 client libraries i.e: clientlib-site and vendors. These client libraries will be loaded in all pages. There is no way to selectively load only certain components OOTB. This leads to sub-optimal lighthouse scores because unused CSS and JS code on page. Hence, there has to be modularity in the clientlibs generated from ui.frontend project. This can be achieved through proper webpack configurations
2. Currently there are two ways to load client libraries
i) At template level: This is recommended because it ensures all CSS wil be loaded at the top (in head) and all JS will be loaded at the bottom. But this gives rise to unused JS and CSS code on page as there is no option to include only those clientlibs which is needed on a particular page.
ii) At component level: This ensures that only required CSS/JS will be loaded. If a particular component is not loaded, corresponding CSS/JS will not load. But, with this approach render blocking CSS and JS code is loaded in body tag which is not recommended.
Hence there needs to be an approach to fix both of this performance issues. If there can be a way to dynamically load client libs (and its dependencies) based on components present on page, this will highly improve page performance.
Current/Experienced Behavior:
All ui.frontend code is bundled into 1 single clientlib and is loaded on all pages
Single vendor client contains many dependencies which may not be required in all pages
Loading clientlibs at template level causes unused JS/CSS to be present on pages. If loaded at component level it causes render blocking calls hampering the performance.
Improved/Expected Behavior:
ui.frontend should have modular structure and there should be component specific client libs.
Vendor clientlib should be generate dynamically to include only necessary dependencies
Ability to load only required clientlibs on page at component level. Also these clientlibs need to be loaded in 1 single request.
Environment Details (AEM version/service pack, any other specifics if applicable):
AEM 6.5.x
Customer-name/Organization name:
Mindtree
Screenshot (if applicable):
Explanation:
Without optimization: Clientlibs loaded at component level.
With optimization: I have used an interceptor to group all CSS in head section and all JS to the bottom of the body tag.
I ask myself, how this should be implemented. I think that it would need an extension of the maven archetyp (ui.frontend module) to create a dedicated clientlib per component, plus an adapted configuration per component.
From my point of view it's hard to to make this generic, because in the archetype itself it's unclear how many components you are going to have and how they are named. I could imagine a "component creation wizard", which creates all necessary structures in ui.frontend, ui.apps and potentially other locations to implement this.
On the other hand, this can also be implemented easily manually (you don't need work done by Adobe) when you create that all manually (or build a script to support you here). Can you create a feature request at the maven archetype project (https://github.com/adobe/aem-project-archetype) and link this proposal?
Thanks for your reply. I have been successful in achieving below feature myself:
ui.frontend generates 1 single client lib - I am able to successfully modularize this using webpack and clientlib config.
When clientlib is loaded at component level (instead of template level), it results in multiple JS/CSS calls in body tag - Using an interceptor I am able to load all CSS on head and all JS at the end of body tag. This has resulted in considerable performance improvement also retaining modularity. (Check the bar graph in my post)
What I am not able to do is, dynamically load vendor dependencies:
Example:
Scenario 1: Page 1 loads Clib1 and Clib2. Clib1 needs Jquery and Bootstrap
Scenario 2: Page 2 loads Clib3 and Clib4. Clib3 needs only Jquery
In this case vendor.js will be same. You will be loading Bootstrap in Page 2 also thereby bloating the page.
Thanks for proposing the idea. If I understand it correctly the suggestion is majorly for the archetype modularity or is there any expectation from the product side as well?
What is needed from archetype modularity perspective?
Ability to generate modular client libraries from ui.frontend folder.
I am able to achieve this by making changes in webpack config and clientlib config. But it will be good to have this in archetype.
What is needed from product perspective?
In order to reduce the number of js/css calls in HTML, the recommended ways to load client libs is to bundle multiple clientlibs into 1 client (using embed) and load it in template level.
Scenario 1
There is a JS/CSS heavy component which is not needed on every page. Ex: shop locator component.
Problem 1: If I load this with embed at template, then this heavy JS/CSS will be unnecessary loaded on pages where it is not needed.
Problem 2: If I load this with at component level, since css/js is render blocking, it goes against the principle of loading css on head and JS at bottom. And if there are multiple such components, it increases the number of CSS and JS.
Hence here is the first expectation from product perspective:
A way (interceptor/transformer?) to collate all component level clientlib inclusion, and pushes the css to head and JS to the bottom of the page while rendering a page.
Scenario 2:
(Extension of scenario 1) There are multiple JS/CSS heavy components which is not needed on every page.
1. Page 1 has comp1 which has dependency1, dependency2
2. Page 2 has comp2 has dependency1, dependency3
If you use webpack to create vendor packages, it will generate a vendor package consisting of dependency1+dependency2+dependency3.
Problem: If you load vendor clientlib on page1 and page2, you are loading unnecessary dependencies on both pages leading to longer page load time.
Hence the second expectation from product perspective:
A way to identify dependencies of clientlibs and bundle the dependencies dynamically.
we had this kind of implementation with another CMS product, where we were loading clientlibs based on component from UI but we used to load those clienlibs(only JS, CSS does not have much impact on performance) as separate request for each/unique component and the reason was -
1. If you combine component clienlibs dynamically and deliver as a single file then you might ending up 1 clientlibs/1 page and thats not good for caching and it is almost hitting publishers.
2. You have to cut off caching for clientlibs in this cash.