Note: Summary at the end.
Recently we were tasked to build a listing page in Drupal which would list down 200+ products on same page. It was already built in the system with pager, search-api facets and sort and some other custom features like showing thumbnail gallery for each product, triggering GA impression for each and many such things. So obviously choice of implementation was to keep using search-api for this to get the advantage of re-using the functionality and to avoid building facets.
First blocker – memory limit reached on server while trying to load these many results in one go, as I said before it is about products so stock check, loading images other checks – it wasn’t simple listing. Also too much of data meant too many facet filters, each filter requires its own URL. We added caches, released memory, and somehow got it done.
QA/UAT starts and first thing, it takes too much of time to load – we said, there are too many results and it went forward.
Now one of the QA applied one filter and browser hanged, both in mobile and desktop, literally hanged for few seconds. At first nobody had any idea on what the reason could be, we started investigating.
We used Chrome’s built in performance audit from dev tools and the results where shocking. Amount of JS executed for each product was too huge. Some basic things which looked simple were resulting into heavy CPU usage.
Issues found and fixes applied for the things with huge impact:
Initiating thumbnail slider for each product on page load and after ajax finish (as product list is replaced)
Requirement: We show only one image by default and show slider of other images (very small size) when hovered over the main image.
Fix: It was not required on load and definitely not required for all the products. We initiated the slider only when hovered, unnoticeable delay in slider showing properly but that was acceptable as it was unnoticeable.
Browser Paint and Recalculate style:
For a specific requirement we had added code to add <style> inside HEAD and add inline styles with value from drupalSettings. This invoked repaint in browser every-time attach was called and was the second biggest reason of our worries.
Requirement: Show menu height based on setting in configuration.
Solution: Simple, we moved the code outside behavior attach, it’s not going to change every-time.
Other problems with major to minor impact which we tackled later or yet to fix as impact was huge and gain was little:
- jQuery.once
Attaching events without once inside Drupal.behaviors.BEHAVIOR.attach can result into multiple attachments of same event and all of them would be executed after the event (for instance $(window).on(‘scroll’))
- Unnecessary code execution
- Missing context while applying JS (jQuery(selector, context))
- JS code executed even if the selected not available on page. Since jQuery won’t give any error it just stays there but even if minor – has impact on browser CPU usage
- Unnecessary code
Lot of code was there just because everything was done in single file and loaded together
Check the images below to give more background on how big the issues became
Summary
- During development itself, make a habit of checking the output of performance tools
- Check page load as well as performance post ajax requests
- Use Drupal.behaviors for only the code which we want to execute after each AJAX request, move the code outside attach for the rest
- Use once and use it wisely to reduce binding of events only once
- Split the code into multiple files and libraries and attach only when required
- Unpopular opinion: write JS in module to allow handling the attachment better
- Question twice before applying styles from JS, is that really required?
- Not talking about show/hide but more about setting height, fonts, etc