Skip to content

Front end optimisation tricks retrospective

Clark Wong edited this page Sep 5, 2021 · 10 revisions

Background

During my development on a local online education platform, I have tried out various tricks to speed up loading of front end. There is 1 question that sticks in my mind: is any of the tricks more effective than the others?

As the platform is already live, it is not possible to undo the changes one by one and compare. Hence setting up this dummy repo to experiment.

Set Up

  • A React project with a few dummy screens
  • A Heroku account with a personal pipeline linked to the Github repository
  • An AWS account

Performance measurement

  • Loading the webpage with webpagetest.org from a EC2 instance in Hong Kong with Chrome
  • Measure time taken for largest contentful paint and file size transmitted till document complete

Tricks

1. Serve production build instead of development build

This was a braindead mistake. For a NodeJS app deployed on Heroku, if a start up script is not specified, the start command in package.json is used as startup command (Heroku Docs). For a project created by create-react-app the start command is react-scripts start, which launches the development server.

What's wrong with the development server?

  • the bundle is not minimised
  • the scripts are not named with hash (bad for caching)

Changes required

Explicitly specify start up command as serve -s build (MR).

Performance

Time taken for largest contentful paint File size transmitted till document complete
Before 5.041s 1573KB
After 2.429s 744KB

2. Bundle optimisation

  • Code splitting: When your website becomes large, you will have more screens in the project. However, for each visit, the user only needs to look at few of them. If we transmit the JS of all screens, we are wasting bandwidth and time.

  • Tree shaking: Most of the time when we import a dependency, we are only using a few functions out of the whole library. "Tree shaking" here means removing the unused part of the dependency, which is usually automatically done by webpack included in react scripts. But it is not the case for lodash. (Stackoverflow Q&A)

Changes required

MR

Performance

Time taken for largest contentful paint File size transmitted till document complete
Before 2.429s 744KB
After 2.388s 725KB

3. Preloading/pre-connecting

For loading a React powered webpage, the browser has to go through (roughly) these steps:

  1. DNS lookup
  2. Connect to target server
  3. Promote to HTTPS connection
  4. Download HTML
  5. While receiving HTML, parse the already received part. Once the <script> tag is parsed, start loading the Javascript bundle
  6. Once the main chunk of the bundle is loaded and if there is a reference to font files, start loading them
  7. Once the font is loaded, start rendering

Changes required

For most cases, font could be loaded without waiting for the JS bundle. (MR)

Performance

Time taken for largest contentful paint File size transmitted till document complete
Before 2.388s 725KB
After 2.324s 725KB

5. Use Nginx instead of serve to serve static files

For our project, the front end is rendered on client side. So all the front end assets are static and a NodeJS server is not necessary. Benefits of using Nginx (Stackoverflow discussion):

  • Slightly faster for individual users: Nginx is implemented in C and directly makes use of system calls to transmit files
  • Scale better for concurrent requests

Changes required

Changing Heroku buildstack. MR

Performance

Time taken for largest contentful paint File size transmitted till document complete
Before 2.324s 725KB
After 2.314s 738KB

6. Content Distribution Network (CDN)

Loading all content directly from Heroku across the globe is slow. What if someone can help serve the static assets form a closer location?

Changes required

  1. Set up a CDN with AWS that caches static contents AWS CDN config
  2. Specify the environment variable PUBLIC_URL of the app to be the domain of the CDN.

Performance

Time taken for largest contentful paint File size transmitted till document complete
Before 2.314s 738KB
After 1.856s 738KB

Takeaways

  1. Be careful not to commit trivial mistakes
  2. $$$ is the most powerful weapon in this battle of optimisation

Further enhancement

  • Optimisation on the 2nd+ load
    • Cache policy
    • HSTS (this is more for security actually)
  • Brotli over gzip