JavaScript
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
138: Lazy Loading and Code Splitting Basics
Why bother splitting my code into different chunks?
Look, when you're first starting out, it feels easier to just have one big app.js file. But as your project grows, that file becomes a monster. If you're using a library like Chart.js or a heavy PDF generator, you're forcing every single user to download that massive amount of code the second they land on your homepage—even if they never actually click the "View Reports" button.
I've seen sites where the initial bundle is 2MB+, and the page just hangs there while the browser tries to parse it all. Code splitting allows you to break that one giant blob into smaller "chunks." You ship the bare essentials first, and then you fetch the heavy stuff only when it's actually needed. It's the difference between handing someone a whole encyclopedia when they only asked for one definition, and just giving them the page they need.
How do I actually implement lazy loading in plain JavaScript?
You're probably used to static imports at the top of your file, like import { calculate } from './math.js';. Those are resolved at load time, which is exactly what we're trying to avoid here. To lazy load, you use the dynamic import syntax: import().
Unlike static imports, import() is a function that returns a Promise. Here is a real-world scenario: imagine you have a heavy analytics.js module that handles complex data processing. You don't want it loading until the user clicks the "Analyze" button.
// This is our main app logic
const analyzeBtn = document.querySelector('#analyze-btn');
analyzeBtn.addEventListener('click', async () => {
try {
// The browser only downloads analytics.js when this line is hit
const analytics = await import('./modules/analytics.js');
// Now we can use the functions exported from that module
const result = analytics.processData(window.userData);
console.log('Analysis complete:', result);
} catch (err) {
console.error('Failed to load the analytics module:', err);
}
});
What should I be splitting, and what should I leave alone?
This is where a lot of developers over-engineer things. You don't need to split every single file into its own chunk; that actually hurts performance because you'll end up making dozens of tiny HTTP requests.
I usually follow a few simple rules of thumb:
- Heavy Libraries: If a module imports a huge third-party package (like a rich-text editor or a mapping API), split it.
- Conditional Routes: If you have a "User Settings" page or an "Admin Dashboard," those should definitely be lazy-loaded. Most users aren't in the admin panel 99% of the time.
- Interaction-based Logic: Code that only runs after a specific user action (like opening a complex modal) is a prime candidate for
import().
If the code is small and used on every page, just keep it in the main bundle. Don't make the browser work harder than it has to.
How do I handle the "gap" while the code is downloading?
Since import() is asynchronous, there is a window of time where the user has clicked a button but the code hasn't arrived yet. If you leave the UI frozen, the user will think the app is broken.
I always recommend a simple loading state. Since we're dealing with a Promise, you can trigger a spinner or a "Loading..." message right before the await call and remove it once the module is resolved. It's a small detail, but it's what makes a professional app feel snappy rather than glitchy.
📋 Practical Task
Build a Lazy-Loaded "Export to CSV" Feature
You are building a data table. To keep the initial page load fast, you need to move the CSV export logic into its own module and load it only when requested.
- Create a file named
exporter.jsthat exports a single functionexportToCSV(data). This function should simplyconsole.log"Exporting data to CSV..." followed by the data provided. - In your main JavaScript file, create a button with the ID
#export-btnand a div with the ID#status. - Add a click event listener to the button that:
- Updates the
#statustext to "Loading exporter..." - Uses a dynamic
import()to loadexporter.js. - Calls the
exportToCSVfunction passing in a dummy array of data. - Updates the
#statustext to "Export Complete!" once the process is finished.
- Updates the
There are no comments for now.