A bundler bundles our application together in one or multiple files, and makes it possible to make code executable in other environments, for example browsers. A bundler receives an entry file, from which it starts to bundle the code together. If we're importing modules from other files, the bundler traverses these modules in order to include them all in the bundle.
Popular bundlers:
webpack, parcel, rollup
Compilers
Converts JavaScript/Typescript code into another version of JavaScript which could be backwards compatible in current and older browser or environments.
Popular compilers:
Babel, TypeScript
Minifiers
Reduce the size of a JavaScript file based on a certain configuration, for example by removes comments, making variable and function names smaller, removing whitespace and so on.
![image](https://github.com/weikee94/design-patterns/assets/13563834/ab13aa03-85a5-4bff-83a9-47e44d1242df)
Popular minifiers:
Terser, Uglify
Combination
There are tools out there combine all steps:
SWC: a Rust based compiler, bundler, minifier
ESBuild: a Go based compiler, bundler, minifier
Bundle Splitting
the process of creating multiple, smaller bundles rather than one large bundler which can lead to an increased amount of loading time, processing time and execution time.
Tree Shaking
reduce bundle size by eliminating dead code.
```js
// input.js
export function validateInput(input) {
const isValid = input.length > 10;
return isValid;
}
export function formatInput(input) {
const formattedInput = input.toLowerCase();
return formattedInput;
}
// index.js
import { validateInput } from "./input";
const input = document.getElementById("input");
const btn = document.getElementById("btn");
btn.addEventListener("click", () => {
validateInput(input.value);
});
```
after tree shaking, the final bundle wont include the formatInput function as it's not referenced in the code.
![image](https://github.com/weikee94/design-patterns/assets/13563834/07edc33b-414c-42ea-a3df-ca6bffb5c5bd)
import code that has been exported by another module, when a module is statically imported, a bundler traverses all the modules, and bundlers them into one file.
```js
import module1 from "./module1";
import module2 from "./module2";
import module3 from "./module3";
```
Dynamic Import
Statically imported modules are all included in the final bundle of our app, even components that don't need to be rendered right away. Depending on the size of the components and the final bundle, this could lead to a worse initial loading experience, as the client has to download and parse the entire bundle.
So in this case we might consider using dynamic import, the Suspense component receives a fallback, that gets rendered while the client is fetching the SearchPopup bundle.
example
```js
import React, { Suspense, lazy } from 'react';
import './styles.css';
import { Card } from './components/Card';
import Card1 from './components/Card1';
import Card2 from './components/Card2';
const Card3 = lazy(() =>
import(/*webpackChunkName: "card3" */ './components/Card3')
);
const Card4 = lazy(() =>
import(/*webpackChunkName: "card4" */ './components/Card4')
);
const App = () => {
return (
Click here to dynamically import {props.name} component
)}
);
}
export default App;
```
[stackblitz](https://stackblitz.com/edit/node-3lzbnw?file=src%2FApp.js)
| Concern | Description |
|----------------------|----------------------------------------------------------------------------------------------------------------------------|
| Faster Initial Load | Dynamically importing modules reduces the initial bundle size, saving bandwidth, and resulting in a faster initial load. |
| Layout Shift | A layout shift can occur if your fallback component and the component that eventually gets rendered differ significantly in size. |
| User Experience | Lazy-loading a component needed for the initial render may lead to longer loading times. Only lazy-load components that aren't visible on the initial render. |
load non critical components when they are visible in the viewport. there is a intersection observer API that can be used to detect whether a component is visible in the viewport.
```js
import { Suspense, lazy } from "react";
import { useInView } from "react-intersection-observer";
const Listing = lazy(() => import("./components/Listing"));
function ListingCard(props) {
const { ref, inView } = useInView();
return (
}>{inView && }
);
}
```
[stackblitz](https://stackblitz.com/edit/node-jy1mlk?file=src%2Findex.js)
| Concern | Description |
|----------------------|---------------------------------------------------------------------------------------------------------------------------|
| Faster Initial Load | Dynamically importing modules reduces the initial bundle size, resulting in a smaller initial load. This saves bandwidth as the client doesn't have to download and execute as much code. |
| Layout Shift | A layout shift can occur if your fallback component and the component that eventually gets rendered have significant differences in size. This can disrupt the page layout and affect user experience. |
Route Based Splitting
dynamically load the components based on route
```js
import React, { lazy, Suspense } from "react";
import { Switch, Route, BrowserRouter as Router } from "react-router-dom";
const App = lazy(() => import("./App"));
const About = lazy(() => import("./About"));
const Contact = lazy(() => import("./Contact"));
ReactDOM.render(
Loading...
Use hints to inform the browser about (optionally) critical resources
##### Prefetch
Fetch and cache resources that may be requested some time soon;A prefetched resource is fetched when the browser is idle and has calculated that it's got enough bandwidth, after which it caches the prefetched resource. When the client actually needs the resource, it can easily get it from cache instead of having to make a request to the server.
![image](https://github.com/weikee94/design-patterns/assets/13563834/75f5c5ad-5f47-45d1-a295-a2a4fe41ea61)
```js
// We can prefetch a resource by explicitly adding it to the head of the html document.
// If you're using Webpack, you can prefetch it dynamically by using the /* webpackPrefetch: true */ magic comment.
const About = lazy(() => import(/* webpackPrefetch: true */ "./about"));
```
[stackblitz](https://stackblitz.com/edit/node-gpwdtl?file=src%2Findex.js)
##### Preload
The preload browser hint can be used to fetch resources that are critical to the current navigation, such as fonts or images are instantly (not longer than 3 seconds after the initial load) visible on a landing page.
![image](https://github.com/weikee94/design-patterns/assets/13563834/65c69f35-77a4-4e42-9faf-164c040a8bfa)
```js
// We can preload a resource by explicitly adding it to the head of the html document.
// If you're using Webpack, you can preload it dynamically by using the /* webpackPreload: true */ magic comment.
const SearchFlyout = lazy(() =>
import(/* webpackPreload: true */ "./SearchFlyout")
);
```
Bundling, Compiling, Minifying, Tree-shaking
Bundler
Compilers
Minifiers
Combination
There are tools out there combine all steps:
Bundle Splitting
Tree Shaking