Development, Events

Performance improvements in Salesforce Lightning components

July 21, 2017

July 21, 2017 by Javier Vázquez

In this post I will show you how to improve your component performance using caching, unbound expressions, and how and when to use the three types of Events available in LEX.

Salesforce Session

Cache it

Often, we are wasting precious time doing requests to the server whose responses we already know. But what if we could recycle these responses? The time saved would mean a lower response time to the client, which is great. Using “action.setStorable();”, the request is only done the first time, and after that we use the same response, which is cached in our callback. This only happens for identical requests: the same method and parameter values.

/** Example: We are getting a contact list to display by pages, displaying 10 contacts 
* in every page, and we know this list is not modified in our component, so it is 
* not necessary to get the list every time we change the page.
* Solution -> cache!
* When we access to the component, the first page is loaded (request done), and 
* when we move to the second page another request is done (pagination attribute has 
* changed). If we then go back to the first page, this is displayed instantly 
* because it is cached and therefore no request is made.
**/
...
getContacts : function(component, event handler) {
 
  var action = component.get("c.getContactList");  
  var pagination = component.get("v.pagination"); // the page we have to display
  action.setParams({"pagination": pagination});    
  action.setStorable(); // activating cache
  action.setCallback(this, function(response) {

    if (component.isValid() && response.getState() == 'SUCCESS') {
      var result = response.getReturnValue();
      component.set("v.contactList", result['contactList']);
      ...
    } else {
      // manage errors
      ...  
    }

  });

  $A.enqueueAction(action);
}
...

Going deeper into this point, we find three different scenarios depending on how much time has passed since the last real request:

< 30s: The callback is executed using the response that was returned the first time.

>30s and <= 900s: The callback is executed using the response that was returned the first time BUT a real request is sent in the background. When the real request is processed, the response is compared with the first response and, if these responses are different, then the callback is executed again using the new response. If the responses are equal, nothing happens.

> 900s: The cached response is already forgotten and a new real request is performed like the first time.

Bound vs unbound expressions

You are probably used to bound expressions, but maybe you are not sure what they are? Well, have you ever seen an expression like “{!v.Contact.Name}” in your components? I’m sure you have.

These kind of expressions are used to display into our components the attribute values, so every time this value is changed in the client or server controller, it is refreshed in the page. And every time the user changes the value in the page, the related variable is updated. This is pretty cool but also quite expensive; because to manage these changes, many events are created, consuming platform resources.

With # instead of !, we are using unbound expressions, so the value is only displayed the first time, and later changes are not mirrored in the page. As no change is possible, the platform is not wasting resources to create and manage events.

<!-- If the contact name and title are modified in the controller, the user will only see how the title is updated -->
...
<aura:iteration items=”{!v.contacts}” var=”con”>
    <ui:outputText value="{#con.Name}"/>
    ...
    <ui:outputText value="{!con.Title}"/>
</aura:iteration>
...

To sum up, use unbound expressions as long as:

  1. A value does not change.
  2. A value might change but you do not want to display it in the page.
  3. You are passing a variable to a child component and the parent component does not want to know anything about the changes.

Use the correct Event type

We have three event types available to us in LEX: Component Event, Application Event, and the new Platform Event. They are very powerful, but it is critical to know where we have to use them if we want to get the best performance.

Component Event: the smallest event, it is fired by a component and can be caught by the same component or by any parent component.

Component Events

In the above diagram, a component event fired by child 2 can be caught by any component in the diagram, and a component event fired by child 1 can only be caught by parent and child 1 components.

The first step is to create our component event. It has a variable to pass a string between components.

<!-- testEvent.evt -->
<aura:event type="COMPONENT">
   <aura:attribute name="message" type="String"/>
</aura:event>

After that, in the child component, we are registering the event we will fire. Notice that the name is used to reference the event in the controller, and the type is the event name.

<!-- childComponent.cmp -->
<aura:component>
...
<aura:registerEvent name="testComponentEvent" type="c:testEvent"/>
...
</aura:component>

Once we have registered the event, we can fire it in the controller.

/* childComponentController.js */
...
 testFunction : function(component, event, helper) {
   var evt = cmp.getEvent("testComponentEvent");
   evt.setParam("message", "OK");
   evt.fire();
   ...
 }
...

In the parent component, we need to define a handler, which will capture this event and execute the associated action.

<!-- parentComponent.cmp -->
<aura:component>
...
<aura:handler name="testComponentEvent" event="c:testEvent" action="{!c.handleEvent}"/>
...
</aura:component>
/* parentComponentController.js */
...
 handleEvent : function(component, event, helper) {
   var message = event.getParam("message");
   // use message
   ...
 }
...

Application Event: the medium event, it is fired by a component and can be caught by any component subscribed to it.
Application Events

 

In this diagram, an application event fired by component 1 can be caught by any component in the diagram. An application event fired by child 1 can also be caught by any component in the diagram, however it is not recommended in this case as our goal is that only component 3 is subscribed to that event. Therefore we should use a component event for that in order to improve the performance.

The code is very similar, so I am commenting only on the differences. The event is created with an application type.

<!-- testEvent.evt -->
<aura:event type="APPLICATION">
   <aura:attribute name="message" type="String"/>
</aura:event>

In the component controller (that is firing the event) the way to reference it is a bit different.

/* firingComponentController.js */
...
 testFunction : function(component, event, helper) {
   var evt = $A.get("e.c:testEvent"); // key point
   evt.setParam("message", "OK");
   evt.fire();
   ...
 }
...

And, in the parent component, we must not define the event name. This is pretty important, if you define a name like we did with component events, the event will never get caught.

<!-- parentComponent.cmp -->
<aura:component>
...
<aura:handler event="c:testEvent" action="{!c.handleEvent}"/>
...
</aura:component>

Platform Event: the biggest event, it can be fired from apex code, flows, process builder or API calls (external system). We can create an apex trigger to handle the event in Salesforce or use CometD to enable an external system to handle it. We will not talk deeply about these events because we would need a full blog post just for it, but I want to leave a comment before finishing: please, do not use Platform Events to communicate with components, use Component or Application events for that.

As you can see, there are best practices around managing performance in Salesforce LEX that we have to understand and internalise, in order to apply them in our daily work. As you develop these new skills and gain fluency, the effort will be worth it, as users will be happier seeing how their Salesforce Lightning apps perform. Happy coding 🙂

Share on Share on FacebookGoogle+Tweet about this on TwitterShare on LinkedIn

Your email address will not be published. Required fields are marked *