TIL: Importing Lodash into Angular, the better way.
Update — December 2019: Here is how I do it to get the smallest bundle size in the prod build.
- 1. Install
lodash-es
instead of the normallodash
.
$ npm install --save lodash-es
- 2. Import a lodash module via default imports
import debounce from 'lodash/debounce';
import zip from 'lodash/zip';
- 3. You might need to set
compilerOptions.allowSyntheticDefaultImports
intsconfig.json
totrue
.
I wrote this post back in 2017. It’s probably not valid anymore but I keep it here as a reference of what I learned.
Update — December 2018: be sure to check out the article: The Correct Way to Import Lodash Libraries — A Benchmark for detailed comparisons between each import method.
I have been working on an Angular app for many months. As the app grows, our team adopt lodash to help us with achieving small tasks with shorter code. Today I learned how to import methods from lodash which produce smaller bundle size from Angular CLI’s build task.
For this post, I create a new Angular project with the CLI tool, install lodash with $ yarn add lodash
. I am using Angular 4.2.5, CLI 1.2.0, and lodash 4.17.4.
This is our typical use case:
import * as _ from 'lodash';_.forOwn((value, key) => {
...
});
_
comes with all methods in lodash. But most of the time, we use only a few methods in our components/services, and the syntax above results in the code imports too many unused methods. The bundle size is unnecessary bigger than it should be. 71.3kB just for forOwn
method is way too much.
Then we discovered another import syntax which looks better — looks like we import only modules we need.
import { forOwn } from 'lodash';
But source-map-explorer still tells us that there is no difference in size than the previous import syntax.
The better way to import a lodash method is this syntax:
import { forOwn } from 'lodash/forOwn';
// or
import forOwn from 'lodash/forOwn';
Note that we are importing from lodash/forOwn
instead of just lodash
.
Check again with source-map-explorer, the size of lodash
comes down from 71.3kB to only 4.5kB. 🎉
and when we zoom in, inside of lodash
, there are only a handful of methods, which are mostly required by forOwn
itself and its dependencies.
That’s cool. But why is that? To find out, check the source files!
When we do import * as _ from 'lodash';
, it uses the file on the left which is the “full build” of lodash — contains everything in lodash in a single file.
While doing import forOwn from 'lodash/forOwn';
uses the file on the right which contains only forOwn
itself. We are “cherry-picking” only methods we want. Therefore, it imports only some parts of lodash we really need, and we get smaller bundle size from the Angular CLI/Webpack build.
Bonus: ES6 module imports & exports
Considering 2 ways to import and export a value in ES6: named export and default export.
Named Exports
Named export is prefixing a value to export with export
keyword. Multiple export
can be in the same file.
// myModules.ts
export const myFunc = function() { }
export class AwesomeClass { }
export interface JustAnotherInterface {}
To use named export, we have to import the values with the same name of what was exported. This is called named import.
// main.ts// import multiple objects
import { myFunc, AwesomeClass, JustAnotherInterface } from './myModules';// or import them separately
import { myFunc } from './myModules';
import { AwesomeClass } from './myModules';
import { JustAnotherInterface } from './myModules';
Note that if we only import 1 value from myModules
, we still need the curly braces { }
. If we omit them, TypeScript throws an error: Module '<NAME>' has no default export.
🤔
Default Exports
When we import something without curly braces { }
, it is then the default import. And as the name implies, it imports the default exported value.
Default exports are created with export default
keywords. The difference is that there can be only 1 default export per file. (In fact, a default export is just another named export with a special name: default
.)
// moduleA.js
export default function() {}// moduleB.js
export default 555;
That’s the overview of ES6’s import
and export
.
Read on: Exploring ES6: Modules
Lodash, however, provides modules in CommonJS module format. I’m still digging into how they are all working together:
- How are CommonJS modules imported in ES6 modules?
- How does TypeScript transpile different module formats and glue them together?
- Or how do default & named exports in ES6 modules relate to single & multiple exports in CommonJS?
I’ll post more updates when I discover more. Any guide through these unknown cave for me is more than welcome. Cheers! 🙏