Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize the library for webpack users #1517

Merged
merged 3 commits into from Mar 23, 2018

Conversation

iamakulov
Copy link
Contributor

@iamakulov iamakulov commented Mar 8, 2018

Hey there! I optimized the library for users who build their browser apps with webpack. webpack is super-popular in front-end development, so that should be a large percent of browser users.

In my tests with map/parallel methods, the library size dropped from 28 to 12 minified kB (−57%). Here’s the repo if you want to reproduce: iamakulov/async-optimization-repro

This is a part of my performance consulting. I help open-source projects (for free) and companies (for money) to optimize the performance of their products: iamakulov.com/perf-consulting


The problem
Right now, when you import async into the app, you import all the methods it includes. So even if you only want to use .map, your app receives all 78 async methods:

import async from 'async';
async.map(...);
// All other methods are present at `async`, even if you don’t need them

Usually, this is solved by importing only the necessary methods:

import { map } from 'async';
map(...);

However, in case with async, this doesn’t help because the async’s entry point, dist/async.js, is a bundle with all the library’s code.

Right now, there’s no easy solution to this problem except doing import map from 'async/map', but that’s not very obvious.

The solution
I added a few flags that hint webpack how to optimize the library:

  • Added "module": "lib/index.js" into package.json
    When the module field is present, webpack uses it instead of main to determine the entry point into the lib. With module, webpack would consume ES modules and drop unused code with tree shaking.

  • Added "sideEffects": false into package.json
    sideEffects: false tells webpack that library modules don’t have any side effects (e.g., don’t modify any globals). Webpack uses this info to remove the module if it’s reexported but not used. This improves the effect of tree-shaking.

  • Removed lib from .npmignore
    Webpack users would need this directory, so we’ll need to publish it to npm.
    This has a drawback – the module would get larger and take longer to install – but, as a benefit, browser users would spend less time downloading it. Browser downloads happen more often than npm installs, so I believe this is for good.

  • Documented the browser usage in readme. Docs are located at http://caolan.github.io/async/, but I’ve thought the performance considerations are important enough to place the snippet in the readme (many users won’t get into docs far enough to read it there). If you don’t want this change, I’d be happy to move it elsewhere.

(Sorry for ads in the beginning, I do freelancing for living)

@iamakulov
Copy link
Contributor Author

@iamakulov iamakulov commented Mar 8, 2018

(I’d also be happy to copy & expand the webpack section into http://caolan.github.io/async/ if this gets merged.)

@aearly
Copy link
Collaborator

@aearly aearly commented Mar 8, 2018

This repo is really used to build 2 modules: async and async-es. If you run make build, it will generate build/ and build-es/ that are exactly what we publish. We already perform similar optimizations to what you propose.

Unfortunately, the package.json in the root is a bit of a red herring -- it is only there so we can continue to support people who use this repo as a git dependency. (otherwise, we wouldn't check in dist/ at all)

@iamakulov
Copy link
Contributor Author

@iamakulov iamakulov commented Mar 8, 2018

Lol. I should’ve looked for that repo :D

I’ve just tried testing this with async-es though, and while the bundle is smaller (16.3 kb vs 28; that’s great!), it doesn’t have the sideEffects: false flag. Adding the flag decreased the bundle down to 11.3 kb.

1. I’d still add the sideEffects: false flag into the async-es’s package.json. Are you open for that?

2. As far as I understood, READMEs are shared between the packages. Would it make sense to link to both async and async-es in the readme, so that users can find the version they need?

A-la “This project exists in two versions. If you use Node.js, install async – it’s built with CommonJS modules out of the box. If you build for web, use async-es – it’s optimized for webpack”

(Ideally, I’d add two different READMEs with slightly different code examples tailored for each version. If you’re open for that, I could tune the build process to do that)

@iamakulov iamakulov changed the title Optimize the library for browser users (−57% of size) Optimize the library for webpack users Mar 8, 2018
@aearly
Copy link
Collaborator

@aearly aearly commented Mar 12, 2018

Both 1 and 2 would be great. We do a bad job of advertising the existence of async-es right now.

@iamakulov
Copy link
Contributor Author

@iamakulov iamakulov commented Mar 13, 2018

Just force-pushed the updates. Looking forward to your review :–)

aearly
aearly approved these changes Mar 14, 2018
README.es.md Outdated

```javascript
// for use with callbacks...
import { forEachFor } from "async-es";
Copy link
Collaborator

@hargasinski hargasinski Mar 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this should be import { forEachOf } from "async-es";

Copy link
Contributor Author

@iamakulov iamakulov Mar 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, thanks! Fixed

Copy link
Collaborator

@hargasinski hargasinski left a comment

Aside from one small typo, looks good.

@iamakulov
Copy link
Contributor Author

@iamakulov iamakulov commented Mar 23, 2018

(Pushed fixes a few days ago :–)

@hargasinski hargasinski merged commit 3235c8f into caolan:master Mar 23, 2018
2 checks passed
@hargasinski
Copy link
Collaborator

@hargasinski hargasinski commented Mar 23, 2018

Merged, thanks!

@iamakulov iamakulov deleted the optimize-for-webpack branch Mar 23, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants