Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Ken Auenson
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Ken Auenson@KenAuenson )

Aggregating Cherry-Picked Lodash Methods In An Application Module In JavaScript

By Ben Nadel on

In an effort to build smaller and smaller JavaScript bundles with tools like Webpack, Rollup, and Browserify, web developers have started to forego full imports of libraries like Lodash; and, instead, are just cherry-picking (importing) individual methods out of the Lodash library. This way, when the bundler does its bundling, it only pulls in the isolated methods instead of the entire parent library. I think this is a noble effort. However, I rather dislike having my modules littered with esoteric variable names. And, to be honest, I miss having a single import for my utilities. As such, I've started to create a local application module that aggregates all of the cherry-picked Lodash methods.

NOTE: My examples happen to use TypeScript, but the theory should be the same regardless of technology choice.

Before aggregating Lodash methods, the use of cherry-picked methods might look something like this:

  • import find = require( "lodash/find" );
  • import without = require( "lodash/without" );
  •  
  • // ...
  • var item = find( collection, [ "id", id ] ),
  • // ...
  • collection = without( collection, item );
  • // ...
  • var item = find( collection, [ "name", name ] ),
  • // ...

As you can see, the find() and without() Lodash methods are being imported individually and then used unscoped in the body of the current module. This totally works. I just don't care for it. I find it harder to read because I don't get to leverage the years of practice I have seeing the "_" variable (and all that it entails). I also find it harder to comprehend how much of Lodash is being used by my application. At first, this may not seem important; however, if I want to factor-out my vendor imports using Webpack, understanding the points of Lodash integration is certainly important.

To find a happy middle-ground, where we walk away with smaller JavaScript bundles without sacrificing the organization of our utilities, I've started moving the cherry-pick imports to a local module I'm calling "lodash-extended" (inspired by Bluebird's "bludbird-extended" philosophy):

  • import find = require( "lodash/find" );
  • import random = require( "lodash/random" );
  • import range = require( "lodash/range" );
  • import without = require( "lodash/without" );
  •  
  • // Repackage and export the individual lodash functions.
  • // --
  • // *********************************************************************************** !!
  • // *********************************************************************************** !!
  • // IMPORTANT READ ME: When you add methods to this list, you must ALSO add them to the
  • // vendor file (main.vedor.ts). This way, as methods get added here, Webpack will know
  • // that they are not application-core methods and can factor them out into a versioned
  • // vendor module, which will help with HTTP caching.
  • // *********************************************************************************** !!
  • // *********************************************************************************** !!
  • export var _ = {
  • find,
  • random,
  • range,
  • without
  • };

As you can see, this Lodash-extended module is little more than glue around the cherry-picked Lodash methods. But, it forces me to understand the app/lodash integration in an important way (for bundle delineation). And, it brings me back to a place where I only need one import to get my utilities:

  • import { _ } from "./lodash-extended";
  •  
  • // ...
  • var item = _.find( collection, [ "id", id ] ),
  • // ...
  • collection = _.without( collection, item );
  • // ...
  • var item = _.find( collection, [ "name", name ] ),
  • // ...

To me, this is nice. I understand that this is totally subjective. But, this is what my brain likes. And, not only does it make it easier for me to consume the utilities, it also makes it easier for me to keep my bundles up-to-date because my lodash-extended module will mirror (in part) my vendor file:

  • // Import these libraries for their side-effects.
  • // --
  • // CAUTION: As you add more "import" statements to your application code, you will have
  • // to come back to this file and add those imports here as well (otherwise that imported
  • // content may get bundled with your main application bundle, not your vendor bundle.
  • import "lodash/find";
  • import "lodash/random";
  • import "lodash/range";
  • import "lodash/without";

And, of course, having a lodash-extended module gives me a nice place to add any additional functions - not provided by lodash - that would benefit my application in a generic way.

Smaller JavaScript bundles are nice. But, creating smaller bundles doesn't mean that we have to sacrifice packaging and established mental models. Aggregating cherry-picked Lodash methods in an application module gives us many of the normal benefits of a cohesive package with only a few lines of additional code. And, it gives us the added benefit of "just the right amount of friction" that will make it easier to keep our vendor files up-to-date.



Looking For A New Job?

Ooops, there are no jobs. Post one now for only $29 and own this real estate!

100% of job board revenue is donated to Kiva. Loans that change livesFind out more »

Reader Comments

Note that you can skip the braces:

import _ from "./lodash-extended";

if you make `_` your default export:

export { _ as default };

Reply to this Comment

@Šime,

Ah, good point. I haven't really dealt much with the "default" export in TypeScript; though, I have used the "module.exports" to set a single export in Node.js. What's cool about the "default" export, though, is that I _believe_ it can live along side other explicit exports.

Reply to this Comment

@Suhas,

Thanks, I'll take a look at this - I've not seen it before. I basically only know enough Webpack to get things to compile for some very basic needs.

Reply to this Comment

@Šime,

Ah, very cool - I didn't realize you could "export { x as default }". I really have to dig more into the default stuff in modules.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
NEW: Some basic markdown formatting is now supported: bold, italic, blockquotes, lists, fenced code-blocks. Read more about markdown syntax »
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.