One thing I hear from time to time is that Lightning Components can be a little slow, especially when rendering dynamic data. In this blog post I’m going to share some thoughts and approaches on how to speed up rendering times and improve the usability of your applications.
Which problem are we trying to solve?
Below are the issues which I typically come across when fixing performance related issues in Lightning Components:
- Initial and subsequent loading times.
- Time taken until the user can interact with the page.
- Initial and subsequent rendering times when rendering changes in the UI.
- Perceived loading time.
This isn’t a bash
Salesforce have done an amazing job with creating the Lightning Component framework. Certainly they can do some more to increase the performance of the overall framework, but they’ve done a great job with some of their latest releases in this respect.
That said, their framework will take you so far. Let’s take an analogy of their framework being a shell of a Farrari sports car and the engine being your code. It’s going to look great, but if the engine is slow, you aren’t going to go anywhere fast anywhere any time soon!
What can we do to speed up Lightning Components?
Learn how to find performance problem areas
You know you have a performance issue and it might be obvious what is causing the problem, however sometimes it isn’t obvious and you’ll need to know how to find problematic areas in your Lightning Components.
- Use console.time & console.timeEnd or even console.profile.
- Use the Lightning Inspector from Salesforce. It has some useful views to visualize how your components are rendered and provides a breakdown of how long components took to render.
- Narrow the problem and focus on one change at a time. Don’t try and solve all performance problems in one go.
- Use the timeline feature in the Chrome developer tools.
Tip: before you begin to optimize your code, first get it working! Don’t prematurely optimise. Secondly, trying to fix performance issues is hard without knowing metrics. Find the problem area, measure and then begin to optimise. Measure again, then adjust, and optimise further.
JavaScript performance
JavaScript now is incredibly fast compared to years ago, but there are certain areas which can definitely slow down the performance of your JavaScript inside your Lightning Components.
- Iterating over arrays or any type of data structure can be a source of a performance degregation. The type of for loop can have a big impact on the responsiveness of the UI and rendering times. Just check out the following Stackoverflow post which shows the many different approaches there are to loops and the performance impact it can have:https://stackoverflow.com/questions/5349425/whats-the-fastest-way-to-loop-through-an-array-in-javascript
- Closures can be memory leaks and not performant. Over on the developer blogs at Google highlight this issue and some other useful performance tips: https://developers.google.com/speed/articles/optimizing-javascript
Server side performance
Your Lightning Components may actually be rendering fast enough but they might appear to be slow due to your server side performance in your Apex controllers.
Consider first how long your transactions are taking to complete. Key areas to pay attention are SOQL and callout durations. If these are taking considerable time to execute they are going to give the user a negative experience. Aim to complete all server side actions within 2 seconds, maximum!
Another aspect to consider is the payload size of the response back from the server. Try and keep the size of the response as minimal as possible, don’t try to send everything just in case. All extra data sent which isn’t used is wasted on how long your Lightning Components take to render.
If you cannot reduce the payload being returned to the user, then consider using Platform Caching. This is a fantastic add-on for Salesforce to really enhance the performance of your overall application! This allows you as a developer to place any type of data structure into the server side cache and retrieve it as it was. Here are some ideas on how you could use it:
- Cache record type information.
- Cache information about the user such as which accounts they are linked to.
- Any utility or org-wide configuration could be cached.
Platform caching isn’t just used as part of controllers for Visualforce or Lightning Components, it can benefit every aspect of your application from the ground up. Consider trigger handlers and their performance, if you can integrate Platform Caching at this layer will not only speed up the UI for users but will also speed up data loads and integrations with external systems.
Client side caching
When designing your Lightning Components consider that if you have a lot of components making requests to the server to fetch data, do they need the most up-to-date data now? Can we cache the results?
Additionally, if you have multiple instances of the same Lightning Component on your page and they all need to make a call to retrieve data from the server can they share the same data source? I.e. can you inject the same dataset into the components?
Storable actions are an easy way to improve performance by caching the results from calls to the server client-side. This means any request with the same parameters to the same Apex controller method will return the cached data instead. Take a look at the following example:
var action = component.get('c.myServerAction'); action.setStorable(); action.setCallback(this, function(response){ // Handle response }); $A.enqueueAction(action);
Tip: you can also ignore the cached data and force the Lightning Component framework to fetch new up-to-date information.
action.setStorable({ ignoreExisting: true });
Another way you can achieve client-side caching is to know that the helper file within Lightning Components is static. This means when you create say 10 new Lightning Component instances of the same component type, they are all sharing the same helper! This is a great place to add caching if you want to impact the performance of all of your components.
Events
There are two types of events I want to talk about:
- Lightning Component Events – the events which are part of the framework design.
- JavaScript events – the raw underlying events which get thrown when interacting with the page.
First, when designing your Lightning Component’s try avoiding application events. Instead use localised component events instead. Application events when thrown cause the framework to process all components which are listening to that event and could cause several areas in the UI to render at the same time! Bad for performance! Component events on the other hand are localised to one branch of a tree of Lightning Components, much smaller impact.
JavaScript events such as onclick or onmouseover should ideally be attached to a parent element to capture the events for all elements which need to trigger the event. This reduces the workload the browser has to handle (especially memory management) when the user is interacting with the page as it a more optimised approach, less event handlers. So for example, if you need to attach an onclick event handler for all list items then attach the event handler to the UL element instead.
<ul aura:id="myList"> <li>Item</li> <li>Item</li> <li>Item</li> <li>Item</li> <li>Item</li> </ul>
Below shows the one and only event listener which is attached to the UL element above.
var ul = component.find('myList').getElement(); ul.addEventListener('click', function(e){ var element = e.target; // Add your logic here... }, false);
Unfortunately, the downside of this approach is that you will need more code to determine which element the user actually clicked on. However, using the above example, if the list grows dynamically to a large amount of list items and this performance technique is not used it when performance issues will arise.
Toggling the visibility of elements
Typically to control the visibility of elements within a Lightning Component you would use an attribute to hold a Boolean value which dictates whether an aura:if should render a panel or not.
<aura:if isTrue="{!v.displayContent}"> <!-- Your content --> </aura:if>
In this case the framework is actually destroying the elements and recreating them each time the Boolean value is updated. This can be a very expensive operation. Though sometimes this is desired, often we can keep the elements there but simply hide them.
The quickest approach for toggling visibility of elements is to use a little bit of JavaScript to add and remove a CSS class from a container. This allows the browser to keep as far down the rendering pipeline as possible as it only needs to repaint the newly applied CSS rules.
Below is an example where we got rid of the aura:if tag and instead replaced it with a simple div tag with an aura ID. At this moment we’ve not hiding the content.
<div aura:id="container"></div>
Now when we want to hide the container and it’s contents we can use the following piece of code. It’s adding a utility CSS class to the container from the Lightning Design System which will hide the element.
var container = component.find('container'); $A.util.addClass(container, 'slds-hide');
Unbound expressions
A very easy and effective way to increase performance in Lightning Components is to use one-way data bindings. Typically, you would use the following syntax to output a label:
{!$Label.c.My_Custom_Label}
Notice that we use {! at the beginning. This instructs the framework to set up a two-way data binding (it’s a bounded expression). This means that if the framework detects a change in any of the referenced value providers (e.g. v. or $Label in this case) then the expression will be recalculated and will cause a rerender to take place. All of this has an impact to performance.
Going back to our example above of outputting a label you should ask yourself during development, is this merge field dynamic? Do I need the latest value to be shown to the user all of the time. If not, such as a custom label, then you should consider one-way data bindings.
{#$Label.c.My_Custom_Label}
This time we start our merge field declaration with {# which is the syntax required to tell the framework to use a one-way data binding. Very simple.
Out of all of the recommendations on this page this one should be your go to solution to start with. It’s very effective and does benefit performance positively and is super simple to implement.
Performance warnings!
Occasionally whilst developing and having your browsers developer tools open you may notice some components reporting performance warnings. Pay attention to these warnings! It’s very easy to ignore the warnings but the framework is warning you are heading towards performance issues if you continue.
Highly recommend reading the following chapters in the documentation to understand why the Lightning Component framework is reporting these as warnings and how to resolve them.
https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/perf_warnings_intro.htm
Ideally, as soon as you start to spot them during development fix them as you come across them! When your application gets large and have multiple moving parts you may become reluctant to fix these issues.
Structuring your components
It’s very to create Lightning Components and I have to admit the guys at R&D at Salesforce have done an amazing job at keeping the framework simple and approachable for new and existing developers.
That said. I think they have made it too easy!
Stay with me and let me explain…
Over at www.lightningdesignsystem.com is where we Salesforce developers are supposed to take our inspiration for creating UI’s that match the overall Salesforce look and feel. So if you need to include a fancy component from the Lightning Design System we simply copy the HTML we’re shown into our components and we’re done. This isn’t specific to the Lightning Design System but just an example.
Why is this a problem?
Performance, separation of concerns and reuse. Every time we copy in HTML into a Lightning Component and have multiple copies of it spread over multiple components the browser has to download it, parse it and then render it, but multiple times for the same thing!
Developing Lightning Components should be seen as building blocks for a larger application. Small blocks which do one thing well and only one thing. The benefit is that these components will be downloaded and parsed once, whilst maximising reuse of the component across your whole application.
Here are some articles to help you structure your Lightning Components:
- https://developer.salesforce.com/blogs/developer-relations/2015/03/salesforce-lightning-components-by-example-component-extension.html
- https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/js_cmp_methods.htm
Assets
It’s quite easy to forget the basics when it comes to improving the load times of web pages when working with Lightning Components. Things such as the following are still just as important when it comes to the assets which are used in the page:
- Image sizes – reduce the file sizes of any images used in the page (a personal favourite is Caesium).
- Minimise any JavaScript referenced in static resources. Every byte counts!
- Minimise also any external CSS. Again, every byte counts!
What to do when all else fails?
There might be a point where you’ve tried your best and are still suffering from performance issues and at this point you really only have a few options remaining:
- Use pagination, don’t try and render so much data at once.
- Avoid iterations where possible and especially nested iterations.
- Consider an alternative approach to how you want to render your UI.
- Lazy load components – create and render component as the user scrolls or when the user clicks into different area in the UI such as when a modal window opens.
Business impact
One thing I haven’t discussed yet is one of the main important reasons for having responsive Lightning Components is what impact it has on business and more seriously, it’s users and potential customers.
Slow loading or perceived slow loading or slow to react web pages irritates end users and will most likely leave before the page has loaded or will complete the required activity then leave, never to return again. This is especially true for customers, but end users within the business will complain and will not be happy and will be reluctant to adopt the webpage inside their day to day activities.
Overall, the bottom line is that slow performing web pages will cost the business money. We need to as developers ensure we deliver high performing web pages to ensure we meet what business demands, which ultimately means more requirements from end users and more development work for us! It’s a happy circle!
More information
Salesforce themselves also provide great documentation and videos on how to get the most out of your custom Lightning Components.
Just recently Salesforce have published their own guidelines on their blog, I recommend you taking a look!
Or if you don’t like reading, check out this YouTube video from Salesforce instead:
Leave a Reply