Making Our Site Loads Faster
Introduction
Every e-commerce site knows that the slower the page it loads the more likely your customers will turn around and leave your site. According to the research from Google, 53% of mobile site visits leave a page that takes longer than three seconds to load.
However, startups like Zola are moving fast to deliver features to meet the needs of our customers.
Once in a while, we have some downtime between projects. I was able to use one of those opportunities to take a step
back to optimize one of the internal React component packages, component-public-registry
.
What are the issues?
- component-public-registry was using webpack 2 and all the JavaScript files were concatenated without any optimization. It builds a bundle that is 3.5 MB!
- Included the unnecessary part of node package, such as
moment/locale
. - When the share components were pulled out from the main app, they carried all the dependency with them cause lots of duplicated packages being included when the library is built.
Step 1: Upgrade to webpack 4
webpack 2 is pretty outdated at this point. We want to start the optimization with better long term support down the road. Our existing webpack config was minimum. It is effortless to upgrade it.
Since webpack 4 minify the JavaScript by default, we can shrink down the size to 865KB without making any webpack config changes.
Step 2: Optimize Less import
Some of the Less files contain incorrect import statements that cause the same CSS file from our internal library to be appended to component CSS files multiple time. By doing this, we reduce the package to 862 KB. Since the CSS file being duplicated is relative small, the size reduction is not dramatic.
Do this:
It will only copy the part being used.
@import (reference) “~@zola/zola-ui/common/_variables.less”;
Don’t do that:
This will copy the entire file which is unnecessary most of the time.
@import “~@zola/zola-ui/common/_variables.less”;
Step 3: Optimize NPM Packages (mostly moment and lodash)
For Lodash:
When we initially created the component-public-registry, we didn't pay attention to how we are importing functions from Lodash. It causes webpack to bundle entire Lodash packages.
Do this:
Only the specific function is imported.
import find from 'lodash/find';
Don’t do that:
Entire Lodash library will be bundled.
import { find } from 'lodash';
For Moment:
Unfortunately, we can’t pick out certain functions like how we did it with Lodash. But that does not mean there is no opportunity for optimization. Since we don’t use locale files from Moment, we can ask webpack not to include that part when making the bundle.
Do this: webpack will ignore the locale section.
plugins: [
// Ignore all locale files of moment.js
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
],
At this point, the file size has been reduced to 582 KB
Step 4: Remove Duplicate Package In Bundle
component-public-registry started in our registry app and our team decided we can use reuse the same code elsewhere.
With limited resources, as mentioned earlier, we only set up a bare minimum webpack config.
As a side-effect, lots 3rd party packages (such as React and Moment) were being bundled without any optimization.
We decide to make those packages peerDependencies
and exclude them from the bundle.
This makes further reduce the package size down to 192 KB.
Do this:
webpack will avoid bundle those packages to reduce the final bundle size.
webpack.config.js
// webpack config
module.exports = {
externals: {
react: {
commonjs2: 'react',
},
'react-dom': {
commonjs2: 'react-dom',
},
'core-js': {
commonjs2: 'core-js',
},
moment: {
commonjs2: 'moment',
},
'@zola/zola-ui': {
commonjs2: '@zola/zola-ui',
},
'logrocket': {
commonjs2: 'logrocket',
},
},
}
Package.json
"peerDependencies": {
"react": ">= 16.8.x < 17.x",
"react-dom": ">= 16.8.x < 17.x",
"core-js": ">= 2.x",
"moment": ">= 2.22.0",
"@zola/zola-ui": ">= 2.*",
"logrocket": "^1.0.3"
},
Step 5: Remove Duplicate Package In Web App
Modern NPM is smart to handle duplicated packages by flattening the dependencies when you do npm install
.
When we inspected the package structure, we still noticed some duplicated packages were being added.
After a bit of a Googling, we found npm dedupe
. According to NPM documentation, the command
searches the local package tree and attempts to simplify the overall structure by moving dependencies
further up the tree, where they can be more effectively shared by multiple dependent packages.
Conclusion
After these iterations, our web app bundle reduced almost 2/3 of its original size. With a much smaller footprint, we also double our Lighthouse performance score for mobile. While this is great news for us, there are many other ways to improve the performance of the website.
I'm glad we have a good start.