Frontend performance pattern
Tags: javascript, frontend, performance, react
Published: 2022-02-18 03:57:41

Original post: https://thanhle.blog/blog/frontend-performance-pattern-en

## Why should you read this?

- Common patterns use to optimize frontend performance
- Boost your web app speed
- Convince your boss and colleague

## When should I care about performance?

First of all, let’s make an agreement that whenever we design a system, structure for an app, it means that we’re making trade-offs. We cut some parts to gain others in order to make the system fit with the problem. What if we want all? It’s impossible, because resources when kick off the project is always limited, however, the problem on the opposite can grow endlessly.

- Centralize vs Decentralize
- Monolithic vs Microservice
- SSR vs CSR
- OOP vs FP
- SQL vs No-SQL
- Language X vs Language Y
- API vs GraphQL
- Stream vs Batch

Then when should I care about performance? And which is the trade-off?

For me, a frontend app has to maintain 3 aspects

- Functional - Your app must run with correct logic
- Maintainable/Readable - If it run right, then it should be easy to maintain and add a new feature
- Performance - it should be fast, delight the user journey

Functional is easy to understand, it is the only aspect that we can not make a trade-off. Then now, our application becomes a slider between `clean code` vs `performance`, it’s depended on each project and the problem to trade-off

<aside>
🐣 You are a guy in the project, so you have the right to know which is more important? Do you want to launch many features at a fast pace or do you want the feature is running lighting fast?

</aside>

> I was really obsessing with performance and had a chance to see that many juniors got the same obsession as me.  Once upon a time, I spend a day paralleling a script that validates the CSV file. The optimization looks good, reduce the time from 15 minutes to 4 minutes. Sadly, the script run only 1 time a week and in turn, it can only save about 40 minutes a month on a free server
> 

## Performance pattern

Here are some common patterns used to optimize performance. And because it is quite popular so it’s quite easy to apply for your project which our sacrifice much on `Maintainenable/Readable`

### Split code/Lazyload

Difficulty: Easy

When to apply: As soon as the project start, we can start with a simple one like split code by pages/routes. After that, if you want to take this further, you can split the code by user interaction[.](https://thanhle.blog/vi/blog/lazy-load-hieu-qua-de-toi-ui-trang-web-hon)

Spell: Only load what users need

How: It depends on your framework, so search Google with this formula: `Framework + code splitting`

Example on React

[Code-Splitting - React](https://reactjs.org/docs/code-splitting.html)

```jsx
import React, { Suspense } from 'react';

const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <OtherComponent />
      </Suspense>
    </div>
  );
}
```

### Prevent installing the duplicated lib

Difficulty: Quite Easy

When to apply: When you start thinking about installing a new library. Then we have 3 options:

- If you use existing lib, pray it to fit with your problem
- If you use new lib, change the legacy code, and pray it to fit with the legacy problem, testing for regression bugs
- Use both libs ⇒ ONLY this is your last hope... like your PM is hanging a knife on your neck

In my current project, we have 3 libraries to handle date-time: `momment`, `date-fns` và `dayjs` . Which `moment` and `date-fns` is big bundle size. 

[You might not need date-fns](https://dev.to/dmtrkovalenko/you-might-not-need-date-fns-23f7)

Spell: Check packages.json before searching/installing new libraries

### Choose the library that supported ES6 and tree shaking

Difficulty: Easy, but depends on community

When to apply: All the time. Bundle size and tree shaking support should be important points to consider.

Spell: The newer library the more chance it is better (But it does not guarantee that it is stable and correctly)

How: Check library on [https://bundlephobia.com/](https://bundlephobia.com/)

 
![`redux` bundle size is 1.6kB when Gzip and supported tree-shaking](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k0p60t56cps0tiam1qot.png)

`redux` bundle size is 1.6kB when Gzip and supported tree-shaking

[Minimizing bundle size - MUI](https://mui.com/guides/minimizing-bundle-size/)

### Debounce user input

Difficulty: Quite easy

When to apply: When we’re hooking user typing, scrolling event which some tasks

Spell: Search input ⇒ Debounce

[Debounce Your Input Handlers | Web Fundamentals | Google Developers](https://developers.google.com/web/fundamentals/performance/rendering/debounce-your-input-handlers)

In more advantage cases, we can use debounce for API response. A common case is to debounce the response for trading/order book on weak computers

### Add `loading=lazy` for tag `img`, `iframe`

Difficulty: Easy

When to apply: Most of the time you see `img` tag, unless you are sure that the `img` is above the fold

Spell: Image + `loading=lazy` ⇒ ✈️

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3k75axhoh7ibrtnx0czo.png)
 

```jsx
<img src="https://www.notion.so/image/https:%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Feed374b2-19d1-444a-b21a-ee686940bb30%2FScreen_Shot_2022-01-21_at_12.45.19_PM.png?table=block&id=a6b45670-a7e6-40bc-ba3d-db40424d6a91&cache=v2" loading="lazy" alt="Nimbus" />
```

### Memorized function

Difficulty: Normal

When to apply: When your function drain lots of CPU and RAM

Spell: Cache the expensive task

[How to use Memoize to cache JavaScript function results and speed up your code](https://www.freecodecamp.org/news/understanding-memoize-in-javascript-51d07d19430e/)

In addition, you can use Web Worker to push those computations into background processes

```jsx
const cachedResult = useMemo(() => {
	// CPU intensive task here
}, [dependencies]);
```

### Cache frontend assets using Service Worker

Difficulty: Normal, hard. It’s quite hard when starting but the result is worst it

When to apply: When you are working in a really big app, bundle size is huge likes complex Admin/CRM

Spell: Complex, big web app ⇒ Service Worker

Example in React

[React PWA with Workbox](https://dev.to/noconsulate/react-pwa-with-workbox-6dl)

Trust me, after you have done this, users will only ever see the loading indicator for the beginning. After that, you can update the app in the background. I will go into detail about how I do it in another post.

### Virtual list

Difficulty: Hard

When to apply: When you have a list containing lots of items. Users have to scroll a while to view all items

Spell: You have a table more than 100 items, you are building something like feed on Facebook, Twitter ⇒ Virtual list

[React Virtual](https://react-virtual.tanstack.com/)

I highly recommend this one. Supper power and lightweight. Forget outdated `react-window`, `react-virutalize` 

When working with `Virtual list`, developers should know about the concept how it works, and also when the component is rerendered to take full power from it. If not, you’re shooting on your foot.

![https://media.giphy.com/media/xvqIOHfQvth5NeoT0Y/giphy.gif](https://media.giphy.com/media/xvqIOHfQvth5NeoT0Y/giphy.gif)

### Break long-run functions into multiple short-run functions

Difficulty: Hard

When to apply: When you run the function, and your laptop hangs 🙃

Spell: Like Above

How: You break your long-run, CPU-bound  function into multiple short-run functions with `setTimeOut` ,`requestAnimationFrame`. However, when breaking long-run functions into many small ones is not an easy task, sometimes your need to keep those functions running sequentially to make sure the function is always correct

### Optimistic update

Difficulty: Easy, normal, hard

Easy when you apply for simple entity

Normal when those entities start conflicting with local and server and you need to solve the conflict

Hard when the logic is quite complex and you have also to deal with solving the conflict on local state and server state

For eg: The like button is easy, the comment is normal, and posting a status is a really hard case

When to apply: When the feature is quite simple. The success rate of the API is about 99.99%

Spell: Simple logic, 99.99% success ⇒ Optimistic update

[Cheat Code for a Lightning Fast Front End: Building an Optimistic UI](https://derekndavis.com/posts/lightning-fast-front-end-build-optimistic-ui)

### Lazy polyfill/Dynamic polyfill

Difficulty: Normal, hard

When to apply: When you’re too tired, don’t have any other option to optimize

Spell: When you see that the polyfill bundle size is quite huge but users are all high-tech

How: Leading right now is Polyfill.io. However, it is quite hard because you need to know how to set up in both frontend and backend

*Which is the pattern that you use to optimize performance? let me know, I’m excited to explore more*