weikee94 / design-patterns

Design Patterns
0 stars 0 forks source link

Performance Pattern #17

Open weikee94 opened 10 months ago

weikee94 commented 10 months ago

Bundling, Compiling, Minifying, Tree-shaking

Bundler

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 Screenshot 2023-10-01 at 4 09 04 PM

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 compilers

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:

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. Screenshot 2023-10-01 at 4 25 40 PM

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)
weikee94 commented 10 months ago

Static Import

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"; ``` Screenshot 2023-10-01 at 5 15 13 PM

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. Screenshot 2023-10-01 at 5 25 27 PM 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 (
); }; function DynamicCard(props) { const [open, toggle] = React.useReducer((s) => !s, false); const Component = props.component; return ( Loading...

}> {open ? ( ) : (

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. |
weikee94 commented 10 months ago

import on visibility

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...
}> , document.getElementById("root") ); ``` [stackblitz](https://stackblitz.com/edit/node-79vjhn?file=src%2Findex.js)

Browser hints

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") ); ```