Code Splitting
by Tomas Trescak· Advanced Topics

0 / 60 XP

Presentation Transcript
If you prefer text to video, check out the transcript of the presentation above

Slide 1 ----------- Part 1: Boost your blogging app’s speed with code splitting and tree shaking! Split JS into chunks for faster loads and trim unused code to shrink bundles. Learn dynamic imports, React lazy, and ES modules with real examples. Debunk myths, optimise like a pro, and make your app lightning-fast! Slide 2 ----------- Part 1: Let's talk about further optimisations for your apps allowing you to limit the bundle that you serve to your clients. Part 2: We know that slow apps lose users. Nobody wants a blogging app that takes ages to load! Optimisation ensures that users stay engaged. Part 3: There are two powerful mechanisms to speed up the loading of your app. First, there is code splitting, which allows the browser to load only what’s needed by dividing JS bundles and sending just the code for the current page, such as the homepage or admin dashboard. Part 4: Second, tree shaking removes unused code from bundles, making them leaner and faster to download. Let's explore them both! Slide 3 ----------- Part 1: Let's explore code splitting first. Part 2: Code splitting divides your app’s bundle so users download only what they need for a page. Part 3: To split the bundle, you have to use javaScript’s dynamic imports to load modules on-demand, supported by tools like Webpack (which is part of NextJS) or Vite. Part 4: A good use case is to load code related to a React component, only when that component is rendered. Part 5: Let's see an example! This code splits our blogging app’s bundle. The homepage loads instantly, while the admin dashboard is fetched only when needed, reducing initial load time. Part 6: This line uses the lazy react component in combination with a dynamic import. The bundler will create a separate JS bundle for all code that relates to the AdminDashboard component. Part 7: Last, we configure our application only to render this component when an admin route is detected. Pure profit since we do not need to send our standard blog users any javascript related to admin functionality! Also, please note that this is crucial in client-rendered components and not necessary for server-rendered components as those send only component-related javascript to the user. Slide 4 ----------- Part 1: Tree shaking is yet another optimisation. The bundler takes care of everything, and you only have to adhere to specific rules to make it work. Let's discuss them! Part 2: Tree shaking eliminates dead code, like unused functions or variables, from the final bundle. Part 3: Remember, it unly works with ES modules using import/export, not CommonJS. The reason is that tree shaking requires static analysis of all imports to estimate which code is not used. Part 4: A good example of tree shaking is when you use large utility libraries like lodash; only the functions you use will make it to the final bundle during build time. Part 5: Let's check out an example, where tree shaking ensures only formatDate is included in the bundle, as sanitizeInput is unused, shrinking the app’s size. Part 6: We have our utility library with two functions, formatDate and sanitizeInput. Part 7: But, in our code we only import the formatDate function. Part 8: When we build our app, we can see how code splitting and tree shaking reduce bundle size. Before using these optimisations, the entire app loads at once. After we load only the homepage code and trim unused utilities, we make the app faster. Slide 5 ----------- Part 1: Let's wrap up! Part 2: Keep your client-side bundle lean and load only what is needed. This improves both application load times and speed. To do this, use dynamic imports, leveraging React lazy and import function for code splitting. Part 3: Tree shaking only works in production builds with bundlers like Vite or Webpack. Make sure you carefully check for changes in your bundle sizes. Part 4: To fully take advantage of tree shaking, avoid side effects in your functions, and try to use only libraries that support ES modules. Avoid common JS in your code at all costs. Follow these practices to optimise effectively. All the best!

Description
All the extra information about this section

LECTURE: OPTIMIZING WEB APPLICATIONS WITH CODE SPLITTING AND TREE SHAKING MOTIVATION Welcome to today’s lecture on optimization techniques in full stack development, specifically code splitting and tree shaking. These are powerful tools to make your web applications faster, leaner, and more efficient—crucial skills for any modern developer. Why does this matter? Imagine you’re building a blogging application where users can browse posts, filter them by tags or dates, and admins can manage content. If the app loads slowly because it’s trying to send a massive JavaScript bundle to the user’s browser, they’ll get frustrated and leave. Slow apps lose users, and nobody wants that! Let’s set the scene with a relatable problem: You’ve built a blogging app, but the initial load takes 10 seconds because the entire JavaScript codebase—client-side views, admin dashboard, and all utilities—is sent to the user’s browser in one giant file. Code splitting and tree shaking can solve this by reducing what’s sent to the browser and removing unused code. For example, why load the admin dashboard code for a regular user who just wants to read a blog post? A common misconception is that simply writing modular code means your app is optimized. Not true! Without deliberate optimization, your bundles might still include unnecessary code. Another myth is that these techniques are only for huge apps like Netflix or Amazon. Even our blogging app can benefit significantly from smaller, faster-loading bundles. Let’s dive in and explore how code splitting and tree shaking can transform our blogging app, making it snappy and user-friendly. SECTION 1: UNDERSTANDING CODE SPLITTING WHAT IS CODE SPLITTING? Code splitting is a technique where you break your application’s JavaScript bundle into smaller chunks, loading only what’s needed for a specific page or feature. This reduces the initial load time and improves performance, especially for users on slower networks or devices. In our blogging app, we have: * Client-side routes: Homepage, post list, filtered post views (e.g., by tag or date), and individual post pages. * Admin routes: Dashboard, post editor, and post management. Without code splitting, the browser downloads everything upfront, even if a user only visits the homepage. With code splitting, we can load the homepage code first and fetch other parts (like the admin dashboard) only when needed. HOW DOES IT WORK? Code splitting leverages dynamic imports in JavaScript, allowing you to load modules asynchronously. Modern frameworks like React and bundlers like Webpack or Vite make this easy. For example, in React, you can use React.lazy() to load components only when they’re rendered. Let’s look at a simple example in our blogging app: * The homepage shows a list of recent posts. * The admin dashboard is only accessible to logged-in admins. Without code splitting, both the homepage and admin dashboard code are bundled together. With code splitting, we can load the admin dashboard code only when an admin navigates to it. EXAMPLE: IMPLEMENTING CODE SPLITTING Here’s how we can split the admin dashboard in a React-based blogging app using React.lazy() and Suspense: // App.jsx import React, { Suspense, lazy } from 'react'; import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import HomePage from './components/HomePage'; // Lazy-load the AdminDashboard const AdminDashboard = lazy(() => import('./components/AdminDashboard')); function App() { return ( <Router> <Suspense fallback={<div>Loading...</div>}> <Routes> <Route path="/" element={<HomePage />} /> <Route path="/admin" element={<AdminDashboard />} /> </Routes> </Suspense> </Router> ); } export default App; Step-by-Step Explanation: 1. Dynamic Import: The import('./components/AdminDashboard') statement tells the bundler to create a separate chunk for AdminDashboard. 2. Lazy Loading: React.lazy() wraps the dynamic import, ensuring the component is only loaded when the /admin route is accessed. 3. Suspense: The Suspense component shows a fallback UI (e.g., “Loading...”) while the chunk is being fetched. 4. Result: Users visiting the homepage don’t download the admin dashboard code, reducing the initial bundle size. ADVANTAGES AND DISADVANTAGES Advantages: * Faster initial load times, especially for large apps. * Better user experience, as pages load only what’s needed. * Efficient use of network resources. Disadvantages: * Adds complexity to the codebase (e.g., handling loading states). * May introduce slight delays when loading new chunks, requiring fallback UIs. * Requires compatible tools (e.g., Webpack, Vite) and proper configuration. ADVANCED EXAMPLE Now, let’s extend code splitting to the post filtering feature. Users can filter posts by tags or dates. Each filter view could be a separate component, lazily loaded to avoid bundling all filter logic upfront. // PostList.jsx import React, { Suspense, lazy } from 'react'; import { useParams } from 'react-router-dom'; const TagFilteredPosts = lazy(() => import('./TagFilteredPosts')); const DateFilteredPosts = lazy(() => import('./DateFilteredPosts')); function PostList() { const { filterType, value } = useParams(); // e.g., /posts/tag/javascript or /posts/date/2023 return ( <Suspense fallback={<div>Loading posts...</div>}> {filterType === 'tag' && <TagFilteredPosts tag={value} />} {filterType === 'date' && <DateFilteredPosts date={value} />} </Suspense> ); } export default PostList; Here, the TagFilteredPosts and DateFilteredPosts components are loaded only when their respective routes are accessed, further optimizing the app. SECTION 2: UNDERSTANDING TREE SHAKING WHAT IS TREE SHAKING? Tree shaking is a technique to eliminate dead code—code that’s included in your bundle but never used. It’s like pruning a tree, removing branches (code) that don’t contribute to the app’s functionality. This is especially useful for removing unused exports from libraries or utility modules. In our blogging app, suppose we use a utility library with functions like formatDate, sanitizeInput, and generateSlug. If we only use formatDate, tree shaking ensures the other functions aren’t included in the final bundle. COMMON MISCONCEPTION Students often think that importing only what they need (e.g., import { formatDate } from 'utils') automatically optimizes the bundle. Not quite! Without tree shaking, the entire utils module might still be included. Tree shaking requires specific conditions, like using ES modules and a bundler that supports it (e.g., Webpack, Rollup, or Vite). HOW DOES IT WORK? Tree shaking relies on the static structure of ES modules (import/export). Bundlers analyze the code to determine which exports are used and exclude the rest. For tree shaking to work: * Use ES modules (not CommonJS). * Enable production mode in your bundler (e.g., mode: 'production' in Webpack). * Avoid side effects in modules that could prevent tree shaking. EXAMPLE: IMPLEMENTING TREE SHAKING Let’s create a utility module for our blogging app and demonstrate tree shaking. // utils.js export function formatDate(date) { return new Date(date).toLocaleDateString(); } export function sanitizeInput(input) { return input.replace(/<[^>]*>/g, ''); } export function generateSlug(title) { return title.toLowerCase().replace(/\s+/g, '-'); } Now, in our PostList component, we only use formatDate: // PostList.jsx import { formatDate } from './utils'; function PostList({ posts }) { return ( <div> {posts.map(post => ( <div key={post.id}> <h2>{post.title}</h2> <p>Posted on {formatDate(post.date)}</p> </div> ))} </div> ); } export default PostList; Step-by-Step Explanation: 1. ES Modules: The utils.js file uses export, making it tree-shakable. 2. Selective Import: We only import formatDate, not the entire utils module. 3. Bundler Analysis: In production mode, Webpack or Vite analyzes the code and sees that sanitizeInput and generateSlug are unused. 4. Result: The final bundle excludes sanitizeInput and generateSlug, reducing its size. To test this, students can build the app in production mode and inspect the bundle (e.g., using Webpack’s bundle analyzer). They’ll see that only formatDate is included. ADVANTAGES AND DISADVANTAGES Advantages: * Smaller bundle sizes, leading to faster load times. * Works automatically with modern bundlers in production mode. * Encourages modular code design. Disadvantages: * Limited to ES modules; CommonJS modules aren’t tree-shakable. * Side effects in modules (e.g., global state changes) can prevent tree shaking. * Requires careful module design to maximize benefits. ADVANCED EXAMPLE In the admin dashboard, we might use a library like Lodash. Instead of importing the entire library, we can import specific functions to enable tree shaking: // AdminDashboard.jsx import { debounce } from 'lodash-es'; // Use lodash-es for ES modules function AdminDashboard() { const handleSearch = debounce((query) => { console.log(`Searching for ${query}`); }, 300); return ( <input type="text" onChange={(e) => handleSearch(e.target.value)} /> ); } export default AdminDashboard; By using lodash-es and importing only debounce, we ensure that unused Lodash functions (e.g., map, filter) are excluded from the bundle. SECTION 3: COMBINING CODE SPLITTING AND TREE SHAKING Code splitting and tree shaking complement each other. Code splitting divides the bundle into smaller chunks, while tree shaking ensures each chunk contains only the necessary code. In our blogging app: * Code Splitting: Separates client routes (e.g., homepage, post list) from admin routes (e.g., dashboard). * Tree Shaking: Removes unused utilities or library functions from each chunk. EXAMPLE: OPTIMIZED BLOGGING APP Let’s combine both techniques in a more complete example. Below is a simplified structure of our blogging app, assuming we’re using React, React Router, and Vite. // App.jsx import React, { Suspense, lazy } from 'react'; import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import HomePage from './components/HomePage'; import PostList from './components/PostList'; const AdminDashboard = lazy(() => import('./components/AdminDashboard')); const TagFilteredPosts = lazy(() => import('./components/TagFilteredPosts')); function App() { return ( <Router> <Suspense fallback={<div>Loading...</div>}> <Routes> <Route path="/" element={<HomePage />} /> <Route path="/posts" element={<PostList />} /> <Route path="/posts/tag/:tag" element={<TagFilteredPosts />} /> <Route path="/admin" element={<AdminDashboard />} /> </Routes> </Suspense> </Router> ); } export default App; // components/PostList.jsx import { formatDate } from '../utils'; function PostList({ posts = [] }) { return ( <div> {posts.map(post => ( <div key={post.id}> <h2>{post.title}</h2> <p>Posted on {formatDate(post.date)}</p> </div> ))} </div> ); } export default PostList; // utils.js export function formatDate(date) { return new Date(date).toLocaleDateString(); } export function sanitizeInput(input) { return input.replace(/<[^>]*>/g, ''); } Vite Configuration (to enable tree shaking and code splitting): // vite.config.js export default { build: { rollupOptions: { output: { manualChunks: { admin: ['./src/components/AdminDashboard'], tagFilter: ['./src/components/TagFilteredPosts'], }, }, }, }, }; What’s Happening: * Code Splitting: The AdminDashboard and TagFilteredPosts components are lazy-loaded, creating separate chunks. * Tree Shaking: The sanitizeInput function in utils.js is excluded because it’s unused. * Vite Config: The manualChunks option ensures admin and tag filter code are in separate chunks, enhancing code splitting. Students can test this by running npm run build with Vite and inspecting the output in the dist folder. They’ll see separate chunks for admin and tagFilter, and the bundle analyzer will confirm that sanitizeInput is excluded. CONCLUSION KEY TAKEAWAYS * Code Splitting: Breaks your app into smaller chunks, loading only what’s needed for a page or feature. Use React.lazy() and dynamic imports to implement it. * Tree Shaking: Removes unused code from your bundles, making them leaner. Use ES modules and production mode to enable it. * Combined Power: Together, these techniques drastically reduce bundle sizes and improve load times, as seen in our blogging app. * Best Practices: * Use modern bundlers like Webpack or Vite. * Write modular, side-effect-free code for maximum tree shaking. * Test your bundles with tools like Webpack Bundle Analyzer to verify optimizations. COMMON PITFALLS TO AVOID * Don’t rely on modular code alone; configure your bundler for tree shaking. * Avoid CommonJS modules, as they aren’t tree-shakable. * Ensure fallback UIs for code splitting to handle loading states gracefully. * Test in production mode, as development mode often disables optimizations. By applying code splitting and tree shaking to our blogging app, we’ve made it faster and more efficient, ensuring users get a smooth experience whether they’re reading posts or managing content. Keep experimenting with these techniques in your projects, and you’ll see significant performance gains!
Maggie

Discuss with Maggie
Use the power of generative AI to interact with course content

Maggie is a generative AI that can help you understand the course content better. You can ask her questions about the lecture, and she will try to answer them. You can also see the questions asked by other students and her responses.

Discuss with Others
Ask questions, share your thoughts, and discuss with other learners

Join the discussion to ask questions, share your thoughts, and discuss with other learners
Setup
React Fundamentals
10 points
Next.js
10 points
Advanced React
Databases
10 points
React Hooks
Authentication and Authorisation
10 points
APIs
CI/CD and DevOps
Testing React
Advanced Topics