A lot has been written on the web about this, but I’m still going to write this down so I don’t forget.
I managed to get from a ~400kb bundle (the file was not served as gzipped) to a ~50kb bundle (gzipped), which is pretty amazing, in three steps (and some researching).
I mixed up the steps when writing this post, that’s why the file sizes I am reporting are not consistent. I actually went with Step 2, Step 3, and finally, Step 1.
Step 1: Enable gzip in Nginx / Apache (if you are hosting on your own VPS).
https://www.digitalocean.com/community/tutorials/how-to-add-the-gzip-module-to-nginx-on-ubuntu-14-04
Result: bundle file size served by Nginx reduced from ~175kb to ~48kb 😱
Step 2: Avoid importing whole libraries when you just need modules.
Instead of doing
import { times } from 'lodash'
import { format } from 'date-fns'
Which doesn’t import only the times and format modules (when using create-react-app), do
const times = require('lodash/times')
const format = require('date-fns/format')
(I think it has something to do with Webpack’s Tree Shaking feature not being used or I don’t know, I didn’t research this any further.)
Result: bundle size reduced from ~260kb (~75kb compressed) to ~175kb (~48kb compressed) 🎉
Step 3: If possible, switch to Preact.
https://medium.com/@rajaraodv/using-preact-instead-of-react-70f40f53107c
My experience when switching was the same as mentioned in the article above, but my app was also kind of small when I switched, and I did have a little issue.
Since this.setState() is async, I had to change when an event fired after updating the state of my app (using setState‘s callback). This is actually how I should have written it in the first place, so it’s my bad it didn’t work right away.
// inside a component
// somewhere at the top
private _messages: HTMLDivElement | null
async componentWillMount() {
const self = this
// ...
// I had to rewrite this
self.setState({ messages: messagesData.messages })
if (this._messages) {
this._messages.scrollTop = this._messages.scrollHeight
}
// Into this
self.setState({ messages: messagesData.messages }, () => {
if (this._messages) {
this._messages.scrollTop = this._messages.scrollHeight
}
})
}
The issue was basically that the _messages <div> scrolled to the bottom before being populated with messages, which obviously resulted with the div not being scrolled at all.
Bonus: use Preact with create-react-app without ejecting:
Check out https://github.com/timarney/react-app-rewired/. It includes a package for Preact, so you won’t need to eject your app to use it.
Bonus 2: use Preact with create-react-app-typescript without ejecting:
You will have to manually add the alias part mentioned in the article above inside
- /your_project/node_modules/react-scripts-ts/config/webpack.config.prod.js
- /your_project/node_modules/react-scripts-ts/config/webpack.config.dev.js
Result: with Preact, the bundle size dropped from ~300kb to ~175kb (~86kb to ~38kb compressed) 🙈
I just noticed one thing: using yarn add/remove pkg will rewrite the files above, and remove the aliases you added. Using npm i --save pkg will not have this effect.
Hope this helps anybody.