odota / web

React web interface for the OpenDota platform
https://www.opendota.com
MIT License
1.08k stars 390 forks source link

Bundle size optimizations #753

Closed howardchung closed 6 years ago

howardchung commented 7 years ago

Lodash is currently the second biggest source of bundle size (only after dotaconstants, which packs all of the ability data). It's even bigger than material-ui.

We should import specifically only the functions we are specifically using.

These might be doable in just regular ES6 and not require a library at all: https://www.npmjs.com/package/lodash.sortby https://www.npmjs.com/package/lodash.map https://www.npmjs.com/package/lodash.concat

These might be lodash-specific: https://www.npmjs.com/package/lodash.flow https://www.npmjs.com/package/lodash.flatten https://www.npmjs.com/package/lodash.curry https://www.npmjs.com/package/lodash.debounce https://www.npmjs.com/package/lodash.inrange https://www.npmjs.com/package/lodash.findlast .rangeStep (I couldn't find a module for this)

howardchung commented 7 years ago

@masad-frost , @mike-robertson , @micaelbergeron

masad-frost commented 7 years ago

We can use lodash-webpack-plugin with babel-plugin-lodash, then we can do import { module } from 'lodash'

howardchung commented 7 years ago

What are the advantages/disadvantages vs just including the individual packages in package.json?

masad-frost commented 7 years ago

I did some testing lodash is ~20kb gzipped, mui ~150kb gzipped ~40 of those are react.

If you want my 2c, if we're stepping into this optimization territory., here's a couple of suggestions:

  1. We might wanna serve most npm modules in a separate vendor.js bundle. This way whenever we push an update we keep that file cached and we invalidate the main bundle file, that's assuming most updates don't touch npm modules. This might slightly worsen intial loads (serving 2 files instead of 1) but it's better for returning users getting updates.
  2. We can chunk our routes (main/player/match) into different bundles, so we serve only the needed files.

Granted I don't think these are necessary (yet), we're under 1mb gzipped. Doing these right now is probably bike shedding

masad-frost commented 7 years ago

What are the advantages/disadvantages vs just including the individual packages in package.json?

Less headache 🧀

masad-frost commented 7 years ago

I think tree shaking might be a thing, but I'm not sure. Also we get access to fp like rangeStep, and will be imported separatly.

howardchung commented 7 years ago

How did you check the gzipped numbers? I just used webpack -j | webpack-bundle-size-analyzer.

If lodash is only 20kb gzipped this might not be worth optimizing, but we could investigate solutions anyway.

Do we have approximate numbers for how big the vendor.js (and bundle.js) would be if we split it up?

masad-frost commented 7 years ago

I just imported the modules and served them in their own bundles.

I don't have any numbers for vendor.js, I'll take a look soon.

howardchung commented 7 years ago

I was actually able to cut down the uncompressed lodash size (reported by webpack-bundle-size-analyzer) to 200KB from 700KB just by removing all import from lodash (leaving lodash/fp). I guess tree shaking or something lets us avoid importing all the lodash functions as long as there are no imports from lodash.

This just meant replacing debounce and findlast with the modularized package versions.

howardchung commented 7 years ago

Notes on further bundle size optimizations:

Current uncompressed bundle size is 3.25MB. Compressed is 903KB.

Top few dependencies (uncompressed via webpack -j | webpack-bundle-size-analyzer):

<self>: 1.7 MB (25.6%)
dotaconstants: 983.32 KB (14.5%)
material-ui: 726.41 KB (10.7%)
react-dom: 535.16 KB (7.90%)
c3: 362.67 KB (5.35%)
d3: 328.93 KB (4.85%)
core-js: 239.28 KB (3.53%)
lodash: 203.33 KB (3.00%)
react: 133.67 KB (1.97%)
redux-responsive: 130.45 KB (1.93%)
commonmark: 107.67 KB (1.59%)

Self:

Dotaconstants:

c3/d3:

blukai commented 7 years ago

c3/d3: Might be possible find a lighter graphing library, but would require some significant code changes.

As an option: https://github.com/mozilla/metrics-graphics But I didn't found tooltips there, may be bad thing

howardchung commented 6 years ago

It seems the lodash/fp module contains the debounce and findlast functions, so I was able to remove those deps for some savings.

Optimally, we'd still remove lodash as a dependency to save more space, but probably the returns from this are less than figuring out how to avoid bundling dotaconstants abilities or language files.