Apollo-client: Find low-hanging fruit to minimize bundle size

Created on 20 Sep 2016  ·  16Comments  ·  Source: apollographql/apollo-client

The bundle size keeps growing slowly. It would be good to make an effort at reducing the size during a refactor, by checking the size of external dependencies and seeing what large packages we could replace with something else.

Most helpful comment

Hi, it would be great if this issue could be re-opened regularly. In our current Next.js app's main bundle, the Apollo Client is taking an increasingly large place. Here is a picture worth a thousand words:

image

Out of 178 kbz of shared dependencies in our node_modules, here is a breakdown:

  • apollo-client: 12.38 kbz
  • apollo-link-rest: 8.88 kbz (this one is brand new, so maybe it hasn't been optimized yet)
  • apollo-cache-inmemory: 5.63 kbz
  • react-apollo: 4.58 kbz
  • apollo-utilities: 3.83 kbz
  • apollo-link: 2.53 kbz
  • graphql: 2.19 kbz
  • apollo-link-http: 1.75 kbz
  • apollo-link-persisted-queries: 0.74 kbz

For a total of 42.51 kbz, not including possible shared hoisted dependencies like lodash. We also plan on adding more, like apollo-cache-persist for instance.

We're obviously big fans of the Apollo Client, deeply thankful for the library, and I hope you'll take this as coming from a good place.

(And yes, there is a lot more to say about our breakdown, and moment.js is going out very soon).

All 16 comments

This is a pretty urgent issue; the current versions of apollo-client and react-apollo seem to add 178kb (minified) to a bundle:

screen shot 2016-12-14 at 6 52 02 pm

lodash is a mess here, I know a PR recently got merged, but perhaps more can be done? lodash should be used very sparingly client side.

For some reason graphql-tag is pulled in anyway when I only use the webpack loader. Is that necessary?

Once you add essentials such as react, react-dom, react-router, polyfills etc. along with your own application code you get ~500kb minified.

@jaydenseric is there an easy way to set up a script that will generate the above image for some set of packages? That would be super helpful.

Also, I think the gzipped size is the most relevant number, which is only 50kb?

I think this will get rid of some of the huge things like lodash.countby etc: https://github.com/apollostack/graphql-anywhere/pull/21

We could also probably eliminate the graphql-tag dep when using the webpack loader for queries, but I'm curious to see how much space the actual parsed graphql queries take up. Looks like this diagram doesn't include any queries at all.

Thanks for reopening this conversation - I find this topic really useful but we currently don't have good enough tools set up to keep track of the size situation, and would really appreciate your help on that.

th0r/webpack-bundle-analyzer is pretty neat, it serves an interactive webpage that can be zoomed and panned after webpack runs. I haven't fully explored the CLI to work out the most efficient way to add a more permanent analyse script to a project. My webpack config returns an array; for seperate server/client bundles. This chokes webpack-bundle-analyzer for some reason so I have to manually comment out the server config every time I run the script.

If you did figure it out a PR to this repository would be super appreciated.

Oh, and keep in mind that those pictures, I think, are post webpack v2 tree shaking and uglify dead code elimination. If users don't have both setup it could be different.

Perhaps in this repo we could:

  1. Setup a subdirectory with a webpack config file and a single JS file as an entrypoint, containing import apollo-client from '../'. Tools sometimes treat imports from node_modules differently, so we would need to make sure this import would behave the same for the purposes of analysis.
  2. Add webpack v2 and and webpack-bundle-analyzer as dev dependencies.
  3. Add an analyse script to the package.json that runs the above in production mode.

It would be interesting to be able to see before and after tree shaking / dead code elimination, perhaps it could run a build for 2 different configs and open the analysis in two tabs using different ports.

To be honest though I doubt there is much a difference with and without tree shaking.

But that strategy seems like a good direction to go. So I just removed all lodash deps from graphql-anywhere, I wonder how much that will help.

Related to tree shaking: https://github.com/apollostack/apollo-client/issues/746 (add module entry to package.json).

I think I just saved 7kb (minified and gzipped - removed lodash.countby and lodash.merge): https://github.com/apollostack/apollo-client/pull/1043

BTW, we removed lodash for a lot more savings in #1122

I think we've taken care of what qualifies as low-hanging fruit for now, so I'll close this issue. 22.7Kb is pretty small, if you ask me!

Hi, it would be great if this issue could be re-opened regularly. In our current Next.js app's main bundle, the Apollo Client is taking an increasingly large place. Here is a picture worth a thousand words:

image

Out of 178 kbz of shared dependencies in our node_modules, here is a breakdown:

  • apollo-client: 12.38 kbz
  • apollo-link-rest: 8.88 kbz (this one is brand new, so maybe it hasn't been optimized yet)
  • apollo-cache-inmemory: 5.63 kbz
  • react-apollo: 4.58 kbz
  • apollo-utilities: 3.83 kbz
  • apollo-link: 2.53 kbz
  • graphql: 2.19 kbz
  • apollo-link-http: 1.75 kbz
  • apollo-link-persisted-queries: 0.74 kbz

For a total of 42.51 kbz, not including possible shared hoisted dependencies like lodash. We also plan on adding more, like apollo-cache-persist for instance.

We're obviously big fans of the Apollo Client, deeply thankful for the library, and I hope you'll take this as coming from a good place.

(And yes, there is a lot more to say about our breakdown, and moment.js is going out very soon).

Edit: I followed up with an issue on apollo-link-rest because it looks like it's duplicating apollo-utilities and apollo-link instead of sharing them with other dependencies.

Likewise apollo-link embarks its own zen-observable even though it is already required by apollo-client and apollo-link-rest.

❯ yarn why zen-observable
yarn why v1.6.0
[1/4] 🤔  Why do we have the module "zen-observable"...?
[2/4] 🚚  Initialising dependency graph...
[3/4] 🔍  Finding dependency...
[4/4] 🚡  Calculating file sizes...
=> Found "[email protected]"
info Has been hoisted to "zen-observable"
info Reasons this module exists
   - Hoisted from "apollo-client#zen-observable"
   - Hoisted from "apollo-link-rest#apollo-link#zen-observable-ts#zen-observable"
info Disk size without dependencies: "148KB"
info Disk size with unique dependencies: "148KB"
info Disk size with transitive dependencies: "148KB"
info Number of shared dependencies: 0
=> Found "apollo-link#[email protected]"
info This module exists because "apollo-link" depends on it.
info Disk size without dependencies: "104KB"
info Disk size with unique dependencies: "104KB"
info Disk size with transitive dependencies: "104KB"
info Number of shared dependencies: 0
✨  Done in 1.08s.

Is there any plan to move apollo-link and apollo-link-rest inside this monorepo and/or keep dependencies in sync?

@damusnet

I also noticed that zen-observable is included multiple times, but it's already in my project in addition the below instances, although I see one version below is the typescript version. These are the non gzipped sizes.

screen shot 2018-05-16 at 1 32 24 pm
screen shot 2018-05-16 at 1 32 09 pm

Hi @sbrichardson,

Since I commented on that (already closed) issue, I landed a PR with apollo-link-rest to exclude peerDependencies, but I have not started anything to improve the situation with the apollo-client and its multiple packages.

I don't think anyone is going to see this closed issue, and I would recommend opening a new one with your bundle size map chart. Personally, I am currently not working in a project with apollo anymore, and can't pursue this at the moment.

Cheers

Was this page helpful?
0 / 5 - 0 ratings