Contents
Overview
JavaScript affects more than download size. It impacts:
- Data usage
- CPU performance
- Memory consumption
- Battery life
For LRO users, JavaScript is often the largest performance bottleneck.
Principle: Keep JavaScript as small and minimal as possible.
Why does this matter for LRO?
JavaScript has four core costs:
- Download – network transfer
- Parse – converting code to structure
- Compile – preparing code for execution
- Execute – running logic and updating UI
On low-end devices, these steps can be 3–5× slower, significantly delaying interactivity.
Common Problems
Network Issues
- Large bundles slow down loading
- Too many files increase requests
- Third-party scripts add delays
Parse & Compile Issues
Importing entire libraries increases processing time.
Avoid:
// Imports the entire lodash library
// This increases bundle size and adds extra parsing/compilation cost
import _ from "lodash";
Prefer:
// Imports only the required function instead of the whole library
// Reduces bundle size and improves load + execution time
import { debounce } from "lodash-es";
Execution Issues
Frequent DOM updates and heavy loops block the main thread.
Avoid:
// Loop runs many times and updates the DOM on each iteration
// Each update triggers layout recalculation (reflow), which is expensive
for (let i = 0; i < 10000; i++) {
document.body.innerHTML += "<div></div>";
}
Prefer:
// Create a document fragment (in-memory container)
// This avoids direct DOM manipulation inside the loop
const fragment = document.createDocumentFragment();
for (let i = 0; i < 10000; i++) {
// Create elements and append to fragment instead of the DOM
fragment.appendChild(document.createElement("div"));
}
// Append all elements to the DOM in a single operation
// This minimizes reflows and improves performance
document.body.appendChild(fragment);
Hidden Costs
- Memory leaks
- Battery drain
- Poor input responsiveness
Core Optimization Techniques
Write Efficient JavaScript
- Prefer native browser APIs
- Minimise re-renders
- Cache DOM queries
- Use event delegation
// Attach a single event listener to a parent container
document.querySelector("#menu").addEventListener("click", (e) => {
// Check if the clicked element matches the target selector
if (e.target.matches("button")) {
// Handle click for the specific button
handleClick(e.target);
}
});
Use Better Code Architecture
- Break into small modules
- Lazy-load non-critical features
- Separate vendor and app logic
// Load the chart module only when it is actually needed
if (userWantsChart) {
// Dynamically import the chart code (lazy loading)
// This reduces initial bundle size and improves load performance
import("./chart.js").then(module => {
// Execute the render function after the module is loaded
module.render();
});
}
Set a JavaScript Budget
| Metric | Target | Max |
|---|---|---|
| Initial JS | <100KB | 200KB |
| Total JS | <300KB | 500KB |
| Third-party JS | <50KB | 100KB |
| TTI (3G) | <3.8s | 7.3s |
| TBT | <200ms | 600ms |
Reduce Bundle Size
- Remove unused dependencies
- Analyze bundles
- Avoid duplicates
Minify Code
Removes unnecessary characters without changing behaviour.
Before minification:
// Readable function with descriptive names and formatting
function calculateTotal(price, tax) {
return price + tax;
}
After minification:
// Minified version with shortened variable names and no extra spacing
// Functionality remains exactly the same
function a(b,c){return b+c}
Tools for minification:
- Terser
- Webpack
- Rollup
- Vite
Use Code Splitting
Load only what is needed.
Example: Lazy Loading a Component (React)
// Lazily load the Visualisation component only when it is rendered
// This prevents adding heavy code to the initial bundle
const Visualisation = React.lazy(() => import('./Visualisation'));
Optimise Script Loading
- Use defer and async
- Avoid blocking scripts
- Compress files
// Adding defer attribute to defer particular js file from loading
<script src="main.js" defer></script>
Lazy Load Non-Critical Features
Non-critical features should only be loaded after the user interacts with the page. This reduces unnecessary data usage and CPU processing on initial load.
// Wait for the first user interaction (click) before loading optional functionality
document.addEventListener("click", async () => {
// Dynamically import the module only when needed
const module = await import("./optional-feature.js");
// Initialize the feature after it is loaded
module.init();
});
Break Long Tasks
A single task that takes longer than 50 ms to run is classified as a long task. If the user attempts to interact with the page or an important UI update is requested while a long task is running, their experience will be affected. An expected response or visual update will be delayed, resulting in the UI appearing sluggish or unresponsive.
// Function to process a large array of items without blocking the UI
function processItems(items) {
let i = 0;
// Chunked processing function
function chunk() {
// Process up to 100 items per frame
for (let j = 0; j < 100 && i < items.length; j++, i++) {
handle(items[i]); // Process individual item
}
// If there are more items, schedule the next chunk
if (i < items.length) {
requestAnimationFrame(chunk); // Runs in the next animation frame
}
}
// Start processing the first chunk
chunk();
}
Choose Lightweight Frameworks
| Option | Recommendation |
|---|---|
| Vanilla JS | Best for simple features |
| Svelte | Excellent |
| Preact | Lightweight alternative |
| Vue / React | Use with SSR |
| Angular | Use only if justified |
Manage Third-Party Scripts
- Audit regularly
- Load with async/defer
- Self-host when possible
Key Takeaways
JavaScript cost = network + processing + execution
Performance improves by:
- Code splitting
- Lazy loading
- Removing unused code
- Avoiding heavy frameworks
Do / Don't
Do
- Use small modules
- Split code by feature
- Measure performance
- Remove unused JavaScript
Don't
- Ship large frameworks unnecessarily
- Include unused libraries
- Overuse third-party scripts
Checklist
Metrics & tools
To ensure LRO compliance, continuously monitor:
- Bundle size (compressed JS)
- JS execution time
- Time to Interactive (TTI)
- Total Blocking Time (TBT)
These metrics reflect real user experience on constrained devices.
Further reading
- How To Optimize Front-end JavaScript Performance | DebugBear
- Optimising JavaScript Bundle Size | DebugBear
- Minify JavaScript And CSS Code For A Faster Website | DebugBear
- JavaScript performance optimization - Learn web development | MDN
- Practical Strategies to Optimize JavaScript Code and Boost Web Performance | by Frontend Master
- JavaScript Optimization - Bundle Size, Tree Shaking & Code Splitting | BulkAudit
- JavaScript Start-up Optimization | Articles | web.dev