3

I'm trying to diagnose a performance problem, and we have isolated it down to a rendering cycle around a lightning-combobox. The architecture is roughly as follows:

  • TopLevel - component that organizes multiple different types of child component
  • SourceLevel - child component dispatches an event up which contains the relevant data
  • TargetLevel - child component which has the combobox
    • Has an @api enabled attribute with an options property

    • This options property is bound to the combobox with a getter

      get options() { return this.someAttribute && this.someAttribute.options; }
      

As the size of options grows, performance exponentially degrades. Based on some profiling, if we represent with x the number of options, the execution time in milliseconds is roughly 1.86 x^2 - 77.95 x + 923. With 125 items, it was averaging over 20 seconds to execute. The picklist that brought this problem to light has 136, and execution time was about triple the above for most of the day.

We tried swapping out the combobox for a custom component loosely based on lwc-combobox-autocomplete by Ben Edwards. This custom component uses <ul> and <li> tags with no lightning-combobox anywhere, and suffered similar performance problems.

We're not sure where else to look at this point to investigate/remediate this performance issue. But I would like to know if others have experienced similar performance problems binding picklists to large collections of options (100+). Or if there is some issue with the binding structure, it is not immediately obvious to me how to fix it. I can add any information still missing if needed.

4
  • How do you get the options? Could it be an apex issue retrieving the options instead of a frontend issue? Commented Oct 13, 2022 at 12:48
  • No, we have isolated the problem to when we assign the value in the success callback. As soon as that line fires, it bricks the UI for the long delay.
    – Adrian Larson
    Commented Oct 13, 2022 at 12:54
  • Just to clarify; this is related to browser rendering thread performance, right? You don't have any plugins listening for DOM mutations do you?
    – Phil W
    Commented Oct 13, 2022 at 13:13
  • This is almost certainly a duplicate of this. Possible solutions: try enabling Lightning Web Security or use a setter and copy the data at each level before passing it on or using the data (use the copied version).
    – sfdcfox
    Commented Oct 13, 2022 at 13:31

1 Answer 1

3

To prove it's not LWC itself, I wrote the following code:

<template>
    <lightning-combobox label="Select Option" options={options} placeholder="Select an Option"
        onselectoption={handleOptionChange} class="slds-m-bottom_small"></lightning-combobox>
    <c-combobox-autocomplete label="Select Option" options={options} placeholder="Select an Option"
        onselectoption={handleOptionChange} classes="slds-m-bottom_small">
    </c-combobox-autocomplete>
    Performance:<span class="output"></span>
</template>

import { LightningElement } from "lwc";

const options = [...Array(1000).keys()].map((key)=>({ label: `Option ${key}`, value: key }))

export default class App extends LightningElement {
  options = options;

  selectedOptionValue;
  selectedOptionLabel;

  constructor() {
    super();
    performance.mark('start');
  }
  renderedCallback() {
    performance.mark('end');
    this.template.querySelector('.output').innerText = ` ${performance.measure('start', 'end').duration}ms`;
  }
  handleOptionChange(event) {
      this.selectedOptionValue = event.detail.value;
      this.selectedOptionLabel = event.detail.label;
  }
}

Your milage may vary, but I'm getting an output of a rendering time of 0.2 milliseconds between constructor and renderedCallback. For 1,000 items. For both lightning-combobox and combobox-autocomplete, running at the same time.

I suspect that you're running into this problem. Locker Service is known to have abysmal issues with performance. Try Enabling Lightning Web Security or using the solution provided in the first link of this paragraph, namely, copying the elements at each level:

get options() { return [...(this.someAttribute?.options || [])]; 
}

Edit: I added a child component to use an @api attribute, and it changed the render time from 0.2ms to 0.3ms. The boundary layer hardly adds any extra rendering at all, which should be further proof.

Demo.

3
  • My read of the documentation indicates you can only Enable Lightning Web Security in an Org if it contains no Aura. Is that a correct interpretation of the first sentence? I did fix the issue by cloning the right attribute, though it was still tricky to figure out where.
    – Adrian Larson
    Commented Oct 13, 2022 at 14:11
  • @AdrianLarson You can enable it, but it's not recommended at this time. See When to Enable Lightning Web Security for specifics, but the idea is that there are some issues when communicating across the Aura/LWC boundary with LS/LWS. At minimum, you'd need to test to make sure LWS doesn't break anything. I've turned LWS on in a few orgs, nothing broke, despite having Aura. Just make sure you test thoroughly.
    – sfdcfox
    Commented Oct 13, 2022 at 14:27
  • 2
    Yeah that won't fly up the chain. 😅
    – Adrian Larson
    Commented Oct 13, 2022 at 16:08

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .