So I'm working on a micro-frontend architecture POC right now, using Web Components to wrap around code from any other framework out there. The goal is to have pieces of the UI be individually deployable to different hosts and simply pulled into the "parent" app (ie, the one that the user navigates to).
I've got most of my architecture working, but right now I'm trying to integrate the Shadow DOM into my work. My current design is to load both the JS and CSS through global static link/script tags, as shown below. Without the Shadow DOM, this works perfectly.
<html>
<head>
<link rel="stylesheet" href="http://micro-fe.com/file.css" />
</head>
<body>
<web-component></web-component>
<script src="http://micro-fe.com/file.js"></script>
</body>
</html>
Once I mount the content inside my Web Component using the Shadow DOM, however, this breaks down. The stylesheet I am loading in the page header is no longer able to touch the content within the Web Component. That is my ultimate goal for using the Shadow DOM, but that means I need a different way of loading my CSS. The goal is to load it from an external stylesheet like it is now, and not have it inlined in a tag. Something like this:
// Code is inside Web Component. "this" is HTMLElement
const shadowRoot = this.attachShadow({ mode: 'open' });
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'http://micro-fe.com/file.css';
shadowRoot.appendChild(link);
I haven't tested that yet, but I've read that tags are supposed to work from within the Shadow DOM. This, in theory, will get me my scoped styles in the Web Component.
My final challenge, and the reason I am posting this question, has to do with conditional rendering. My won't always be on the page. Other logic around it will determine when I want that content rendered. So my concern is that I don't want the CSS file to have to be re-loaded each time the component gets rendered.
I'm considering browser caching as a possible solution, but in general I'm wondering if there are any tips that can be provided. I know this is a bit complicated and non-standard, but my ultimate goal is to solve all of these problems and publish a library that does it all out of the box to make it easier for others.
I haven't tested that yet, but I've read that tags are supposed to work from within the Shadow DOM. This, in theory, will get me my scoped styles in the Web Component.
Yes, you can use <link> inside Shadow DOMs.
So my concern is that I don't want the CSS file to have to be re-loaded each time the component gets rendered. I'm considering browser caching as a possible solution, but in general I'm wondering if there are any tips that can be provided.
Yes, thanks to browser caching the CSS file will only be downloaded the first time it is needed.
Related
And injecting all the design on the head like JSS does, it has some benefit on performance?
If you are asking whether adding javascript and css inside of the header rather than in the body gives some form of performance improvement, it does not. It just matters whether the files will be loaded before or after the body is created. Some utilities require you to even put script tags after a certain line of html inside of the body so that the library will be able to see the needed DOM element.
Everything gets loaded before the user sees the page whether that is in the head or in the body. The only way to have things loaded truly dynamically, that I know of, is to use javascript to add script tags or iframes that will be loaded at a later time.
So, no there is no performance improvement.
Do you mean critical path for SSR?
I'm new to AEM. Currently we have one template for each page on our site. All components have the category "project_name.components" and I am calling the client libs in a header file with:
<sly data-sly-call="${clientLib.css # categories='project_name.components'}" />
<sly data-sly-call="${clientLib.js # categories='project_name.components'}" />
However, I have a breadcrumbs component that isn't on every page, but, as expected, the client libs files for it are showing up regardless and causing some issues with the existing default breadcrumbs's styles/scripts.
I have given the new breadcrumb component a test category name of "project_name.breadcrumbs". Is there a way to use this category name in some type of an if/else statement in the same header file that will only call the breadcrumb client lib files if the breadcrumb has been dragged onto the page?
A few thoughts:
The easiest way is to include the client library as part of the component that uses it rather than including it somewhere else. The downside of this is that the CSS you may want to load early in the HEAD section of a page won't be present until somewhere in the BODY.
If your CSS styling is impacting things it shouldn't, then the CSS styling needs to have sufficient selectors so that it won't break things to which it shouldn't apply. Perhaps you can add a class to the breadcrumbs and make all the styling only be applied to stuff under a tag that has the class. If you changed the CSS this way, it wouldn't negatively affect pages when they don't use the breadcrumb (though it could have a downside of bloating your page footprint if it isn't something that will be loaded and browser cached to be used in the future).
Otherwise, you could add logic that runs at the page level that will examine the node and see which components are included and then add conditional logic to only add the client library when the page is using the component. But that is more back-end work. So you can add the if/else statement as you suggested, but it is up to you to write the code behind it--there is nothing built-in that will conditionally do that check to my knowledge.
I'm learning web components with a shadow root and can't seem to find on google if loading external stylesheets is possible with out-of-the-box code? I am NOT using polymer or any other web component library (yet). Code below:
<script src="../../libs/jquery-2.1.1.min.js"></script>
<script>
var hollaProto = Object.create(HTMLElement.prototype);
hollaProto.createdCallback = function () {
var shadow = this.createShadowRoot();
var content = document.querySelector('link[rel=import]').import.querySelector("div");
$("button[data-command=holla]", content).on("click", function () { alert("Holla!"); });
shadow.appendChild(content);
};
var hollaWidget = document.registerElement("holla-back", {
prototype: hollaProto
});
</script>
<div class="holla-back">
<button data-command="holla">Holla!</button>
</div>
If I put my link tag up top, above the first script tag, I style the whole web age, but not the web component.
If I put it under div.holla-back it doesn't style anything.
How do you use external stylesheets with web components?
Link tags are inert in Shadow DOM according to the spec. However, you can use #import, though that has its own performance issues.
The way Polymer works around this is it looks at the link tags and uses xhr to load those styles and apply them.
edit:
The folks working on Shadow DOM are aware of this shortcoming and that it needs to be fixed. Hopefully in the future we can come up with a system that supports external stylesheets.
Shadow DOM doesn't react to link tags. Infact, Chrome 41 throws an error when you use link tags. We have worked around that limitation by inlining CSS classes at the build time using vulcanize. This turned out to be quite handy in separating your CSS and the component definition.
Google PageSpeed Insights is flagging this as something I should fix - I've read the guidance on Optimising CSS Delivery at https://developers.google.com/speed/docs/insights/OptimizeCSSDelivery but I'm confused at what the best practice is, and also on which resources are render blocking and which aren't?
Is Google suggesting removing stylesheet links from the page head and replacing with inline styles to make something render, then using Javascript to trigger an external stylesheet to load when window.onload fires? Won't this just delay process of arriving at the 'correctly rendered' page - isn't it better for the browser to start downloading the CSS as soon as possible?
Yes, that's pretty much what the page you reference is recommending. Put the minimal amount of CSS (as long as it's a small amount) directly in the HTML markup within a <style> tag. Then include the complete set of styles at the end of the document. (In the example, it's not actually loaded via JavaScript per se; rather, the link to the external style sheet is placed in a <noscript> tag. That's a bit of a hack, but it gets the job done. You could also request the stylesheet via AJAX and inject it using JavaScript directly.)
This approach only works if you can isolate the minimal CSS needed for your page and that amount of CSS is reasonably small. If, for example, you're building a single page web app, then many of your CSS rules might be for parts of the app other than the initial view. In that case, those extra rules can be put in the external style sheet. Or maybe you have a set of rules strictly for pop-up dialog boxes. Those rules can be postponed as well.
If you can't really separate your rules into those that are needed initially and those that aren't, and if your minimal rule set is large, you can't take advantage of this approach.
I'm creating an AJAX heavy web application and I'm curious on what people's thoughts are on adding style and script tags to the body of an HTML document.
I've been using the jquery.load() method to insert content on the fly. An example partial HTML page that could get loaded into the body element is this:
<script type="text/javascript">
$(function() {
// All required java script for the control goes here...
});
</script>
<style type="text/css">
/* All required styles for the inserted HTML go here */
</style>
<div id="some-control">
<!-- Required HTML markup is here. -->
</div>
Since this HTML is getting loaded into a DIV, we are ending up with script and style tags that are not in the head but in the body of an HTML document. I'm not sure if this is valid HTML? Is this a common approach to developing AJAX enabled web applications? Are there any drawbacks I should be aware of?
As far as Javascript is concerend, you can put it any where on the page provided that elements it will work upon are loaded before and it does not throw the error undefined element. Famous Yahoo performance article and even Google (in terms of SEO) suggests to put javascript at the end of the page just before the </body> tag.
If you can manage to put your script just before </body> tag, that is considered good approach otherwise what you are doing now should be fine if everything is working properly for you.
I would actually disagree with Sarfraz and say that you should avoid using <script> and <style> tags in your page body as much as possible. The advantages of moving your JS to an external file are endless. The most obvious include:
leveraging on browser caching - if you just write code in your body, that's extra kilobytes of data that need to be loaded for every page. If it's a universal function, you're wasting precious load time. If it were in an external file, most modern browsers cache that file and only request a new version so often. This decreases server load as well (less requests)
Furthermore, if you ARE using a similar script on multiple pages, what happens if you need to make a change :(. Now you're running around searching for every instance of a <script> tag to make a change. Having all your code centrally located and universal allows for ONE change and DONE
Versioning - if you use version control (GIT, SVN, etc), it's much easier to track and revert one file (if you made a mistake or accidentally lost code) than all of them
CSS share a similar story. Again, with caching and centralized storage, and reusability. It's even more important, however, for styles to match on a website. From a UI standpoint, you don't want your fonts changing from page-to-page and you don't want to edit 40 pages every time you want to add a new style.
As far as having the JS in the document because you are using AJAX loaded content, I suggest you look into .bind and .live. They let you attach handlers to existing and future instances of a DOMObject. For example:
$('.class').live('click', function(){
alert('I was clicked!');
});
This will apply to any object that existed at page load AND objects that are later created. The following code will NOT - it only applies to objects created on load:
$('.class').click(function(){
alert('I was clicked!');
});
When I see that the big-site Content Management Systems routinely put some <style> elements (some, not all) close to the content that relies on those classes, I conclude that the horse is out of the barn.
Go look at page sources from cnn.com, nytimes.com, huffingtonpost.com, your nearest big-city newspaper, etc. All of them do this.
If there's a good reason to put an extra <style> section somewhere in the body -- for instance if you're include()ing diverse and independent page elements in real time and each has an embedded <style> of its own, and the organization will be cleaner, more modular, more understandable, and more maintainable -- I say just bite the bullet. Sure it would be better if we could have "local" style with restricted scope, like local variables, but you go to work with the HTML you have, not the HTML you might want or wish to have at a later time.
Of course there are potential drawbacks and good (if not always compelling) reasons to follow the orthodoxy, as others have elaborated. But to me it looks more and more like thoughtful use of <style> in <body> has already gone mainstream.