Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How To Import Lodash for Smallest Build Sizes

I care about my javascript app build sizes. I do what I can (within reason) to keep build sizes as small as possible.

Currently, my imports look like this:

import * as _ from 'lodash';

I've heard that "granular" imports (like below) can improve build size, but I'm not seeing the results I would expect:

import {get, map, set} from 'lodash';

What is the best way to import lodash to achieve small total build size?

like image 569
random_user_name Avatar asked Oct 16 '19 15:10

random_user_name


1 Answers

Build sizes are VERY important for getting users engaged and retained - great question. I have had a similar issue, and these days want to create as small of builds as possible.

I constructed a quick test using source-map-explorer - a simple tool to analyze after you run a build on your project (e.g. Angular, React). While this tool can be very useful for analyzing what is in your bundles, keep in mind it is minified (as your build process would do) but not gzipped. Thus it is not what your users would experience, assuming you are on a good host using mod_deflate or a server with similar gzip/Brotli/etc compression.

Here's what I did to test using a popular library: React. I followed instructions using create-react-app then created several variants and build them using yarn run build.

  • Test 0: create-react-app (non-typescript)
  • Test 1: added lodash via import * as _ from 'lodash'
  • Test 2: removed lodash, added lodash-es and did import { upperCase } from 'lodash-es'
  • Test 3: removed lodash-es, added back in lodash and did import { upperCase } from 'lodash'

After the build process, I did a command line gzip to get the approximate "web user" gzip size. gzip *.js from the build output directory.

My results? Well, the runtime-main does not vary at all. The main varies slightly with different text, such as calling the included function to ensure upperCase is actually "baked in" to the build and not just discarded. I'm not concerned about those sizes so will ignore that.

Thus I'll summarize using the chunk.js.gz size (full results below):

  • Test 0: 41177 bytes (baseline)
  • Test 1: 65540 bytes (+24363 bytes to use one function, ouch!)
  • Test 2: 43203 bytes (+2026 bytes to use one function - much better!)
  • Test 3: 65540 bytes (+24363 bytes again, still hurts)

The conclusion? Use lodash-es for granular importing. Also, for code clarity, and not overriding other function names such as map, I have settled on this strategy:

import { map as _map, get as _get } from 'lodash-es'.

Directory and file sizes thanks to `tree --du`

├── [ 42787] cra-default-test-0
│ ├── [ 41177] 2.284b2af8.chunk.js.gz
│ ├── [ 642] main.a1384886.chunk.js.gz
│ └── [ 808] runtime-main.3ba3833f.js.gz
├── [ 67174] cra-all-lodash-test-1
│ ├── [ 65540] 2.ef604131.chunk.js.gz
│ ├── [ 666] main.c259429e.chunk.js.gz
│ └── [ 808] runtime-main.3ba3833f.js.gz
├── [ 44845] cra-lodash-es-uppercase-only-test-2
│ ├── [ 43203] 2.2852311b.chunk.js.gz
│ ├── [ 674] main.131e67d8.chunk.js.gz
│ └── [ 808] runtime-main.3ba3833f.js.gz
├── [ 67188] cra-lodash-uppercase-only-test-3
│ ├── [ 65540] 2.0ccbb0bd.chunk.js.gz
│ ├── [ 680] main.c3d7426c.chunk.js.gz
│ └── [ 808] runtime-main.3ba3833f.js.gz
like image 194
ryanm Avatar answered Sep 29 '22 12:09

ryanm