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

Switch to lodash + lodash-webpack-plugin ... #5616

Merged
merged 18 commits into from
Jul 27, 2017
Merged

Conversation

benvinegar
Copy link
Contributor

@benvinegar benvinegar commented Jun 24, 2017

  • Removes underscore in favor of lodash
  • Uses lodash-webpack-plugin + babel-lodash to minimize filesize impact
  • Saves ~110kb unminified between removing underscore (~50kb) and using lodash-webpack-plugin to generate a partial lodash build (another ~60kb)

A big reason we did this was because we have some dependencies which were requiring lodash anyways, which meant we were bundling both lodash and underscore together 🤐.

I experimented with a bunch of dependency changes:

Dependencies Filesize (unminified)
underscore + full lodash (before) 263kb
underscore + optimized lodash 164kb
optimized lodash only (this PR) 154kb

Interestingly, removing underscore doesn't result in as much benefit as you'd expect (vs optimizing the lodash build alone), because those functions just end up being transferred to the lodash build.

Note: need to make corresponding changes in getsentry repo, since it uses a global alias to sentry's exported underscore global.

cc @dcramer

#nochanges

@@ -1,4 +1,4 @@
import $ from 'jquery';
import _ from 'lodash';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This wasn't really necessary, it got pulled with some other changes I was exploring (using jQuery's slim build which doesn't have $.extend) ... I can revert.

Choose a reason for hiding this comment

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

../

);
})
.value();
let tuples = _.map(kvData, (val, key) => [val, key]);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The lodash webpack bundle doesn't support chaining, and lodash's .map doesn't play as nice w/ empty objects so I made this more defensive.

@@ -136,6 +136,10 @@ var config = {
]
},
plugins: [
new LodashModuleReplacementPlugin({
shorthands: true,
Copy link

Choose a reason for hiding this comment

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

If you can avoid the shorthands feature it will save you a considerable amount. It's one of the heaviest features.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sold!

@jdalton
Copy link

jdalton commented Jun 24, 2017

You can upgrade to webpack v3 and use new webpack.optimize.ModuleConcatenationPlugin() for the production build to optimize the build a bit more.

@benvinegar
Copy link
Contributor Author

benvinegar commented Jun 24, 2017

You can upgrade to webpack v3 and use new webpack.optimize.ModuleConcatenationPlugin() for the production build to optimize the build a bit more.

Yeah, I'll probably do that separately, and maybe after a few patch releases.

And thanks for your feedback on this PR.

@jdalton
Copy link

jdalton commented Jun 24, 2017

Sold!

I'm curious what the minified bundle numbers are now that you dropped the "shorthands" feature set.

Yeah, I'll probably do that separately, and maybe after a few patch releases.

And thanks for your feedback on this PR.

Cool. When you do keep in mind the ModuleConcatenationPlugin that enables scope hoisting (think rollup) works on ES6 modules. So to get the benefit with your deps, like Lodash, you'll want to use the lodash-es module instead. You can even use webpack to alias lodash requires as lodash-es too.

@benvinegar
Copy link
Contributor Author

So to get the benefit with your deps, like Lodash, you'll want to use the lodash-es module instead.
You can even use webpack to alias lodash requires as lodash-es too.

Good to know.

I'm curious what the minified bundle numbers are now that you dropped the "shorthands" feature set.

I did re-run the numbers, and it turned out that by adding collections, the number jumped up (I didn't update the table), and after removing shorthands, it went back down to the same figure: ~154kb. I ran it again just now to check.

@jdalton
Copy link

jdalton commented Jun 24, 2017

~154kb. I ran it again just now to check.

That's unminified right? Lodash has inline documentation in its unminified form so that is a falsely inflated view. You can minify as well as gzip to get a more prod-centric size.

@benvinegar
Copy link
Contributor Author

benvinegar commented Jun 24, 2017

That's unminified right? Lodash has inline documentation in its unminified form so that is a falsely inflated view. If you minify as well as gzip to get a more prod-centric size.

Also good to know. Yeah, I'm excited for the results – just too impatient to wait for webpack -p to finish 😅

@@ -92,7 +92,7 @@ const OrganizationStats = React.createClass({
});
});

_.each(this.state.rawProjectData, statName => {
$.each(this.state.rawProjectData, statName => {
Copy link
Member

Choose a reason for hiding this comment

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

couldn't se just use .forEach at this point?

Copy link

Choose a reason for hiding this comment

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

couldn't se just use .forEach at this point?

If it's an array yes. If you're only working with arrays then you also don't need the "collections" feature set.

Copy link
Contributor

@MaxBittker MaxBittker left a comment

Choose a reason for hiding this comment

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

we need audit the other uses of underscore that didn't show up in this diff for problems like pick/pickBy (which I only saw because of the mapValues change). glad we're doing this though

})
.value();
return _.map(this.props.kvData, (val, key) => [val, key]).map(([val, key]) => {
return (
Copy link
Contributor

Choose a reason for hiding this comment

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

return _.map(this.props.kvData, (val, key) => [val, key]).map(([val, key]) => {
is equivalent to
return _.map(this.props.kvData, (val, key) => {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Laugh, true

@@ -1,5 +1,6 @@
import React from 'react';
import underscore from 'underscore';
import lodash from 'lodash';
Copy link
Contributor

Choose a reason for hiding this comment

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

use _ here for consistency?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If _ can be used safely in the file without conflicts, yes.

@@ -1,6 +1,6 @@
/*eslint getsentry/jsx-needs-il8n:0*/
import React from 'react';
import underscore from 'underscore';
import underscore from 'lodash';
Copy link
Contributor

Choose a reason for hiding this comment

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

here too, _

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See comment above. If that can't be done, rename to lodash.

@@ -1,5 +1,5 @@
import moment from 'moment';
import underscore from 'underscore';
import underscore from 'lodash';
Copy link
Contributor

Choose a reason for hiding this comment

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

and this one, _

@@ -44,7 +44,7 @@ import ReactDOM from 'react-dom';
import {renderToStaticMarkup} from 'react-dom/server';
import Reflux from 'reflux';
import * as Router from 'react-router';
import underscore from 'underscore';
import underscore from 'lodash';
Copy link
Contributor

Choose a reason for hiding this comment

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

this one too _

Copy link
Contributor

Choose a reason for hiding this comment

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

this one will break the bundle's external API

@@ -1,5 +1,5 @@
import React from 'react';
import underscore from 'underscore';
import underscore from 'lodash';
Copy link
Contributor

Choose a reason for hiding this comment

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

'_' in this file too IMO

@@ -1,6 +1,6 @@
import DocumentTitle from 'react-document-title';
import React from 'react';
import underscore from 'underscore';
import lodash from 'lodash';
Copy link
Contributor

Choose a reason for hiding this comment

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

_ if you please

@@ -1,5 +1,5 @@
import iOSDeviceList from 'ios-device-list';
import {isString} from 'underscore';
import {isString} from 'lodash';
Copy link
Contributor

Choose a reason for hiding this comment

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

do we get static analysis benefits from importing like this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In this case, not really since we're using this webpack plugin.

@@ -163,7 +163,7 @@ const InstallWizard = React.createClass({
let loadingIndicator = IndicatorStore.add(t('Saving changes..'));

// We only want to send back the values which weren't disabled
let data = _.mapObject(
let data = _.mapValues(
_.pick(options, option => !option.field.disabled),
Copy link
Contributor

Choose a reason for hiding this comment

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

pick doesn't doesn't work like this in lodash, we need to do _.pickBy for it to accept a predicate.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If so, please change (and good catch). Let's also test this page (I definitely did not, sigh).

@@ -1,6 +1,6 @@
import React from 'react';
import DocumentTitle from 'react-document-title';
import underscore from 'underscore';
import underscore from 'lodash';
Copy link
Contributor

Choose a reason for hiding this comment

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

just commenting '_' on these because i feel obligated now

Copy link
Contributor

Choose a reason for hiding this comment

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

I would also say it makes it less confusing for new people to know where to google documentation

@MaxBittker
Copy link
Contributor

https://github.com/lodash/lodash/wiki/Migrating was super useful 👍

Copy link
Contributor

@MaxBittker MaxBittker left a comment

Choose a reason for hiding this comment

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

my concerns are addressed in my commits

@MaxBittker
Copy link
Contributor

Do not merge yet. at least comments are broken

@MaxBittker
Copy link
Contributor

update: react-mentions relies on some features we're disabling in LodashModuleReplacementPlugin

@MaxBittker
Copy link
Contributor

figuring out what those are in order to proceed

@MaxBittker
Copy link
Contributor

this is deploying to staging right now for good measure, but I think it's safe to merge. @benvinegar @billyvg

@billyvg
Copy link
Member

billyvg commented Jul 25, 2017

We should probably add an acceptance test for the activity tab, since our suite did not catch these errors

@MaxBittker
Copy link
Contributor

you're right billy, also not the first time this page has hidden a regression now that I think. wrote one off the cuff so I'll see what percy shows

new LodashModuleReplacementPlugin({
collections: true,
currying: true,
flattening: true
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We talked about having a comment about mentioning currying and flattening are enabled to support react-mentions. Can we add that?

* Removes underscore in favor of lodash
* Uses lodash-webpack-plugin + babel-lodash to minimize filesize impact
* Saves ~100kb unminified between removing underscore (~50kb) and using lodash-webpack-plugin over full lodash dep (another ~50kb)

Note: need to make corresponding changes in getsentry repo, since it uses a global alias to sentry's exported `underscore` global.
@MaxBittker
Copy link
Contributor

I'm not sure how you were measuring the bundle sizes, webpack -p gave me:

  app.js          946 kB       0  [emitted]  [big]  app
  vendor.js     960 kB      14  [emitted]  [big]  vendor

@MaxBittker
Copy link
Contributor

 254K Jul 26 15:42 vendor.js.gz
 217K Jul 26 15:42 app.js.gz

@MaxBittker MaxBittker merged commit 2ac81ef into master Jul 27, 2017
@benvinegar benvinegar deleted the lodash-bundler branch February 27, 2018 20:01
billyvg added a commit that referenced this pull request Nov 13, 2019
Undoes the work introduced in #5616. 

This plugin adds around 40-50 seconds to webpack build times and has caused confusion (see #13834).

Also adds an eslint rule to prevent importing `lodash` bundle: getsentry/eslint-config-sentry#46

| -- | With Plugin | Without Plugin | Difference | lodash-es | lodash-es + react-mentions |
| -- | ----------- | -------------- | ---------- | --------- | - |
| app.js | 739051 | 739120 | 0% | | |
| app.js.gzip | 184017 | 184040 | 0% | | 186949 |
| vendor.js | 2076311 | 2090876 | +0.7% | 2089352 |
| vendor.js.gizp | 375718 | 380446 | + 1.2% | 380630 | 380476 |
| build time (w/ stats) | 316s | 466s | + 47% | |
| build time (env=prod) | 335s | 198s | n/a | |

Currently, there is no sizable difference between `lodash-es` and `lodash` -- we should revisit this in the future.
@github-actions github-actions bot locked and limited conversation to collaborators Dec 22, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants