Lightning Web Components – Performance Comparison Between LWC Reactive Property and JavaScript Property

javascriptlightning-web-componentsperformanceproperties

There two ways we can define property in lwc javascript.

  1. We can define reactive property which we can use in HTML page. This reactive property stays throughout the lifecycle of the LWC component.
  2. Using native javascript/ ECMAScript notation i.e var,let and const.

When it comes to the 2nd option, this kind of property can be defined in two places :

i) Block level scoping i.e inside any JavaScript method or in conjunction with if-else ,for Loop etc. The lifecycle of this JavaScript property is till we are inside the blocked scope, after that garbage collector do the memory management for these properties.

ii) Defining the property between Import and export statement in javascript controller file, which has a global scope i guess ( I am not sure about being global or not). But what i am sure that this global property stays through out the lifecycle of the lwc component.

So we have two property that stays through out the lifecycle of the LWC component

  1. Reactive property
  2. Global JavaScript property

So my question is, when we doesn't need to bind the property to HTML and need to keep the javascript property available throughout the component lifecycle which is the best way to define the property in terms of Memory Management and run time performance?

Best Answer

For completeness, there are six distinct scopes. They are, in order of largest to smallest access: global, import, component, instance, closure, and local.

Global scope is only available in LWC OSS. Every component in every namespace can use values from this scope. As far as I'm aware, there's no component-accessible objects available at this scope in Salesforce. You cannot use truly global variables inside Salesforce. This scope's duration is effectively forever, as long as the app is loaded.

Import scope is storage used for an imported module. This will persist at least as long as any component that imports the module is loaded, and likely last as long as the Lightning App Runtime is loaded. This scope can be used for inter-component communication, such as pubsub, because all components share the same import scope.

Component scope is storage used for any objects declared outside of the class. It exists as long as any instance of a specific component is loaded. It will last at least as long as any component instances are loaded, so it may be shorter than import scope's duration.

Instance scope is storage used for a specific instance of a component, declared inside the class. You've called this "reactive properties," which would also be a true statement. Objects in this scope are only guaranteed to exist as long as that component isn't destroyed, typically shortly after disconnectedCallback, though there is no specific information on when the memory will be freed.

Closure scope is storage used temporarily while a function is pending in some way. For example, if you use a Promise, a Closure is created that will last until the Promise is fully resolved. For a properly written component, this scope will exist longer than any local scope, but shorter than any of the other scopes, which is why it is placed here.

Local scope is storage used by a function, and will not exist after a function runs to completion. It is the shortest scope and has the smallest visibility. We use this to perform local calculations, etc.

Note that garbage collection in JavaScript is mostly pressure-based anyways, so it won't GC more than it deems necessary; there's no way to tell for sure when an object will be physically removed from memory. We can only make observations about their general longevity relative to each other.

The smaller the scope that data is placed in, the less accessible it is, so the more copies of that data may be necessary. So, from a Memory Usage and Run Time Performance perspective, placing an object in a larger scope will guarantee it is duplicated less, but it will also be in memory longer.

A library module is evaluated only once, even if imported in to many components. In other words, it is the most memory efficient and runtime efficient of all the options you have in Salesforce. Likewise, component scope variables are evaluated only once when the first instance of a component loads, so it is less efficient than import scope, but more efficient than instance scope. This property is true all the way down.

While it's rarely ever necessary to note, this means that you should not modify any variables stored at the component level; they're marked as const to implicate the fact, but you can indeed use let instead and have the variable modifiable by all instances. Since those properties are not reactive, this can have unintended side effects, so be careful.

You should always use the smallest scope possible to avoid side effects, but the largest scope possible to maximize performance. There is a definite tradeoff that you need to consider when designing your code. In most cases, instance scope is probably the correct scope to use. As long as you're aware of potential side effects, moving the scope upwards can improve performance significantly.

Also keep in mind that markup can generally only access instance-scoped data (reactive properties), so this limits the the other scopes mostly to handlers. You need to copy data from the larger scopes to a reactive property to use them locally, and since those larger scopes can be modified if you're not careful, you may end up needing to make copies for local use.

Related Topic