0% found this document useful (0 votes)
85 views13 pages

Lightning Components Performance Best Practices

Uploaded by

Alberto Einstein
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
85 views13 pages

Lightning Components Performance Best Practices

Uploaded by

Alberto Einstein
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 13

Lightning Components Performance

Best Practices
Lightning Components run at the client-side, in a single page (where they are
created and destroyed as needed), and alongside other components that work on
the same data. In this blog post, we discuss how these characteristics impact
performance, and review a list of best practices to optimize the performance of your
Lightning Components.
If you are just starting with Lightning components, consider completing the following
Trailhead modules before diving deeper:

• Quick Start: Lightning Components

• Lightning Components Basics

The list of performance best practices discussed in this document includes:

• Data retrieval

• Data caching

• Component instantiation

• Conditional rendering

• Data binding

• Lists (aura:iteration)

• Events

• Third-party JavaScript libraries

• Base Lightning Components

• Image optimization

• Rendering and reflow

• Development settings vs production settings

• Performance profiling tools

Data retrieval
Optimize server round-trips:
• Before making a call to the server, make sure there’s no other option to obtain
the data.

• When appropriate, consider passing data between components (using


attributes, events, or methods) rather than retrieving the same data in different
components. The Lightning Data Service (currently in Developer Preview and
in Beta in Summer ’17) also allows you to efficiently share data between
components.

• When making a call to the server, limit the columns and rows of the result set:

• Only SELECT the columns you need.

• Set a LIMIT on the query and provide a paging mechanism if needed.


Don’t return huge numbers of rows at once.

• Lazy load occasionally accessed data. Don’t preload data that the user may
never ask for (for example, data hidden behind a tab the user may not click, or
in a combobox the user may not open).

• Client-side filtering and sorting: Don’t make a call to the server to filter or sort
data you already have at the client-side.

• Consider combining several requests (actions) in a single composite request.

• Cache data when possible (see Data caching).

Data caching
Application composition is a powerful way to build apps by assembling self-contained
components. However, without proper planning, the autonomous nature of the
components you assemble can have an adverse impact on performance. For
example, if all the components you build make their own isolated calls to the server
to retrieve the data they need, you’ll probably end up with lots of redundant server
calls, which can dramatically impact performance.
Client-side data caching can solve that problem by sharing data among components.
This can significantly reduce the number of server round-trips, and improve the
performance of your Lightning components. The Lightning Component Framework
has two built-in mechanisms for client-side caching (storable actions and Lightning
Data Service). You can also implement a custom caching solution.

Storable actions
A storable action is a server action whose response is stored in the client cache so
that subsequent requests for the same server method with the same set of
arguments can be accessed from that cache. Server actions enable you to access
data using a traditional rpc approach: You implement some logic in Apex that you
expose as a remotely invocable method. Storable actions enable you to cache
virtually anything (whatever the server method call returns): a record, a collection of
records, a composite object, a custom data structure, data returned by a callout to a
third-party service, and so on.
The general guideline is to cache (mark as storable) any action that is idempotent
and non-mutating.
More information:

• Read the Caching Data with Storable Actions blog post

• Check out the DreamHouse sample application for examples. Storable actions
are used in the following
components: PropertyListDaysOnMarketChart, PropertyTileList,
and SimilarProperties

Lightning Data Service


Lightning Data Service provides a managed record approach: you’re not
responsible for writing data access logic (no Apex). The framework is responsible for
managing records: fetching them from the server when requested the first time,
storing them in a highly efficient client cache, sharing them between all components
that request them, and sending changes to the server. Lightning Data Service also
handles progressive loading: it only loads the requested fields. If another component
subsequently requires additional fields, these fields are loaded transparently and
added to the record in the cache. Unlike storable actions that can cache any type of
response returned by an Apex method, Lightning Data Service caches discrete
Salesforce sObjects (record collections and other data types like metadata are on
the roadmap). Lightning Data Service also improves user interface consistency:
when a component updates a record, all the other components using that record are
notified, and in most cases refreshed automatically.
More information:
Check out the following components in DreamHouse to see the Lightning Data
Service in action: PropertyMap, PropertySummary, MortgageCalculator.

Custom cache
You can also implement your own custom cache approach. As always, make sure
you don’t reinvent the wheel and only use a custom cache approach when there is
no standard way to implement your caching requirements in the framework. For
example, a custom cache can be a good solution for static data like a list of states in
the United States, picklist values, and so on. You can use a storable action in this
case too, but it still generates unneeded server calls (every time the data in the
cache is older than the refreshAge, currently set to 30 seconds in the framework). If
you know that the data won’t change, or changes infrequently, you can implement a
custom cache solution that retrieves the data once after the user logs in and then
never goes back to the server.
Check out the Mutual Fund Explorer app for an example of a custom cache. Open
the DataCache static resource (File > Open > Static Resource > Data Cache in
the developer console). And then check out
the SectorSelector and AssetClassSelector components to see how it’s used to
cache the list of sectors and asset classes.
More information:
Read the Modularizing Code in Lightning Components blog post for strategies to
implement a custom cache.
Summary of client-side caching options:

Caching Requirements Recommen

Single record Lightning Data Service

Collections of records, composite responses, custom data structures, third-party data Storable actions
Complete control over caching implementation Custom cache

Component instantiation
Showing every available piece of data and every available tool on the screen just in
case the user may need it is generally not considered a good UI practice. It can also
significantly impact the performance of your application. Today’s interactive design
guidelines favor progressive disclosure.
“Progressive disclosure is an interaction design technique often used in human
computer interaction to help maintain the focus of a user’s attention by reducing
clutter, confusion, and cognitive workload. This improves usability by presenting only
the minimum data required for the task at hand” (Wikipedia). Put another way,
“progressive disclosure defers advanced or rarely used features to a secondary
screen, making applications easier to learn and less error-prone” (Jakob Nielsen).
In Lightning Experience, it’s easy to implement progressive disclosure and defer the
data or features that aren’t essential to the task at hand. Let’s take a closer look at
two approaches to defer component creation.

Lazy instantiation in Lightning Experience


Lightning Components added to a page layout are instantiated when the page is
loaded, contributing to the overall loading time of the page. For the right use cases,
components can be hosted in specific areas in the Lightning Experience where they
are lazily instantiated:

• Quick or Global Actions

• Utility Bar

• App Builder tabs

Lazy instantiation in your own components


• You can use <aura:if> to lazily instantiate parts of the UI (see conditional
rendering section).
• Some components (like <lightning:tabset> and <lightning:tab>)
support lazy instantiation by default.

• You can also instantiate components programmatically


using $A.createComponent(); however, that approach is generally harder
to code, maintain, and debug.

Conditional rendering
There are two popular techniques to conditionally render UI elements:

Toggling visibility using CSS


For example:
<div aura:id="error" class="slds-hide">{!v.errorMessage}</div>
cmp.find("error").toggleClass(!isError, "slds-hide");
or
<div class="{! v.isError ? null : 'slds-hide'
">{!v.errorMessage}</div>
The <div> component and all of its children are created and rendered up front, but
they’re hidden. If there are event listeners attached to the <div> or any of its children,
these event listeners would be “live.”

Conditionally creating elements using <aura:if>


For example:
<aura:if isTrue="{!v.isError}">
<div>{!v.errorMessage}</code>
</aura:if>
In this case, the <div> component and all its children are only created if the value of
the isTrue expression evaluates to true. All the components inside
the <aura:if> tag are destroyed if the value of the isTrue expression changes and
evaluates to false. They’re re-created the next time the value of
the isTrue expression changes again and evaluates to true.

Guideline: use aura:if


The general guideline is to use the <aura:if> approach because it helps your
components load faster initially by deferring the creation and rendering of the
enclosed element tree until the condition is fulfilled. Components inside an <aura:if>
with the value of its isTrue expression evaluating to false aren’t created and aren’t
part of the DOM. Event listeners aren’t created either.
NOTE: The <aura:renderIf> approach is deprecated and shouldn’t be used.
More information:
In the DreamHouse sample application:

• The PictureGalleryCard and Map components use <aura:if> to toggle


between regular and full-screen mode.

• The PropertySummary and SmartPriceCalculator components


use <aura:if> to defer the instantiation of their body until a property is
selected when used in a master/details interface.

Data binding
There are two ways to establish a connection between UI elements and attributes:
bound and unbound expressions.

• Bound expressions provide an implementation of bidirectional data binding.


Bidirectional data binding provides a convenient and powerful abstraction.
However it also comes at a cost: The framework has to set up two event
listeners for each bound expression, which can have a negative impact on
performance when there is a large number of bound expressions. It also
creates an entanglement between the model and the view that can make your
code harder to maintain and issues harder to debug in non-trivial situations.

• Unbound expressions can have a positive impact on performance because


they don’t maintain event listeners. However, they may not be the right solution
for use cases where the UI has to be kept in sync with a changing model.

In the DreamHouse sample application, components created


inside <aura:iteration> use unbound expressions to avoid the proliferation of
event listeners. See PropertyListDaysOnMarketChart for an example. Also see
how PropertyTile is bound to property objects inside PropertyTileList.
If you use data binding as a way to execute logic in a child component, consider
using <aura:method> instead to provide your component with a public API. Check
out the Mutual Fund Explorer app for an example of using <aura:method>.
The FundInfoPopup component provides an API (showPopup and hidePopup) to
show and hide the popup.
More information:
Read the Lightning Inter-Component Communication Patterns blog post for more
details on inter-component communication patterns in general.

Lists (aura:iteration)
When creating custom list components (typically using <aura:iteration>), don’t
support the creation of an infinite number of list items. Either provide a pagination
mechanism, or virtualize the list (reuse and rehydrate a limited number of list item
components).
Check out the Paginator component along with the PropertyController Apex class
in DreamHouse for an example of a paginated list.

Events
Minimize the number of event handlers:

• Use unbound expressions when possible.

• Use <aura:if> when you need to conditionally render UI elements.


Components inside an <aura:if> with its isTrue expression evaluating to
false aren’t created, which means that event listeners attached to these
components aren’t created either.

• Limit the use of Application Events to coarse-grained application-level


communication. For example: Communication between components added to
pages in App Builder.

• For finer-grained communication between components:

• Use Component Events for child-to-parent communication:

• <Paginator onPageNext="{!c.pageNextHandler}"
• onPagePrevious="{!c.pagePreviousHandler}"/>
• Use attributes or <aura:method> for parent-to-child communication

• When working with lists, letting events bubble, and registering a single event
listener on a parent element instead of a separate event listener on every list
item can significantly reduce the number of event listeners in your application,
which can have a positive impact on performance. In the Mutual Fund Explorer
application check out the FundTileList component, and see how a
single onmousemove event listener is registered on the list element (<ul>)
instead of a separate listener on every list item (<li>) (inside
the FundTile component).

More information:

• In DreamHouse, see the PropertyTileList and Paginator components for an


example of inter-component communication using a component event.

• See PropertyTileList and PropertyMap for an example of using


an application event to communicate between components in App Builder.

Third-party JavaScript libraries


Remove dependencies on unneeded libraries
Before you decide to use a third-party library in a Lightning component, you should
reevaluate if you really need that library. DOM manipulation libraries (like jQuery)
and UI libraries (like Bootstrap or jQuery UI) in particular may no longer be needed
when working with the Lightning Component Framework.
DOM manipulation libraries
JavaScript has come a long way in recent years. Many DOM manipulation utilities
we couldn’t live without in libraries like jQuery are now standard in the language.
Modern frameworks like the Lightning Component Framework also provide
abstractions that make jQuery less relevant.
UI libraries
You may also want to reconsider the use of UI libraries like Bootstrap and jQuery UI.
Although these libraries provide useful components, they have their own UI identity
that can clash with the Lightning Experience identity. The Base Lightning
Components and the Lightning Design System offer similar capabilities while
providing a consistent user experience.
MVC frameworks
At a high level, libraries like React and AngularJS have the same focus as the
Lightning Component Framework: They provide code organization and utilities to
create components. Using another MVC framework together with the Lightning
Component Framework inside a component isn’t recommended. You can, however,
use a Lightning component as a container to host components built with other
frameworks (like React and AngularJS) inside the Lightning Experience, but this is
outside the scope of this document. See LockerService and Lightning Container
Component: Securely Using Third-Party Libraries in Lightning Components blog post
for details.
Use minified versions of libraries and style sheets
When you use a third-party library, make sure you use minified versions of the library
and style sheet.
More information:
Read Building Lightning Components Chart.js blog post for an example of how to
use Chart.js in Lightning components.

Base Lightning Components


There are two sets of core user interface components:

• The original components that were made available when the Lightning
Component Framework first came out live in the ui namespace
(<ui:button>, <ui:inputText>, and so on).

• The new and improved components, also known as the Base Lightning
Components, live in the lightning namespace
(<lightning:button>, <lightning:input>, and so on).

Whenever possible, use the Base Lightning Components (components in


the lightning namespace). Benefits include:

• Styles: Base Lightning Components are styled with the native Lightning look
and feel.

• Performance: Base Lightning Components are already loaded at the client-


side and don’t require additional download or processing. Our performance
optimization efforts are also focused on components in the lightning
namespace.

• Innovation: The Lightning namespace is where components are being actively


developed. This is where you can expect to see new and improved
components moving forward.

• Accessibility: Base Lightning Components are built for accessibility.

• Client-side validation: Base Lightning Components include client-side


validation when applicable.

Image optimization
• When possible, use the (sprite-based) Lightning Design System icons
(using <lightning:icon> and <lightning:buttonIcon>) instead of
custom icons.

• Lock image dimensions (to avoid reflows) and serve the image in those
dimensions when possible. For example, don’t load a high-resolution image to
display a thumbnail.

Rendering and reflow


• Understand when and why your component is being rerendered. Read
the Rendering Lifecycle and Events Fired During the Rendering
Lifecycle sections in the documentation.

• Minimize the number of times your component is being rerendered.

• Lock DOM regions to specific dimensions to avoid browser reflows of


surrounding areas.

• Avoid direct DOM manipulation.

Development settings vs production settings


Optimizations for a production environment and a development environment are
different. Here is a typical question asked in the developer forums:
“When I make changes in a component, I need to refresh the page a few times in the
browser for my changes to take effect. Why do I have to do that, and how can I avoid
it?”
The reason is that to optimize performance in a production environment, component
definitions are cached at the client-side. As a result, when you make a change to a
component, and then reload the page hosting the component, you can still get the
previous version of the component served from the client cache. You can usually get
the new version of the component by refreshing the page a couple of times, but that
isn’t a suitable developer experience. The solution is to disable client-side caching
during development.
Recommended settings:
Development Production

Debug mode On

Component caching Off

• To enable/disable debug mode: Setup > Custom Code > Lightning


Components

• To enable/disable component caching: Setup > Security > Session Settings


> Caching, and uncheck Enable secure and persistent browser caching to
improve performance

IMPORTANT NOTE: Client-side caching and debugging are org-wide settings.


Disabling client-side caching and enabling debugging has a significant impact on
performance. Only do this in your development environment, not in a production
environment. Similarly, if you run tests to assess the performance of your
environment, remember to turn these settings back to the production values.

Performance profiling tools


If you face a performance problem in a component, make sure you understand what
the real problem is before trying to solve it. Don’t make assumptions. Use profiling
tools to identify the performance bottlenecks.

• Profile Components with Chrome Timeline

• Use the Salesforce Lightning Inspector Chrome Extension

• Analyze with the Salesforce Community Page Optimizer


Summary
The performance of an application is impacted by many different factors. The
performance optimization techniques described in this article are general guidelines
that should help you build applications that are faster and more responsive. Try them
out in your application, and let us know what your favorite performance optimization
techniques are.

You might also like