Meteor speed wishlist (bundle size improvements)

Meteor 1.5’s code splitting is technologically awesome achievement by @Benjamin for big apps. But there are still ways Meteor could reduce bundle size & parsing time that I’d love to see.

  1. Remove jQuery, as a dependency of any core package, so React apps can fully exclude it (95kb) (EDIT: Already done in Meteor. Jquery was a dependency of one of Flow Router’s deps. Solution to remove it: Meteor speed wishlist (bundle size improvements) - #50 by viejo)
  2. Tree shaking in build step to remove unused JS (https://rollupjs.org/)
  3. Remove es5-shim as a dep of any core package so it can actually be removed for devs who don’t want to support IE8. Removing via meteor doesn’t remove it from the project, it must be a dependency of another package.
  4. Use one of either lodash or underscore. IIRC both lodash and underscore used in core packages as a holdover from the past.
  5. When it’s production ready, evaluate https://prepack.io/ in the build chain because it can reduce JS evaluation time which is currently a significant portion of app load time.

1 and 2 are the biggest.

What else is on your wish list? Any time estimates for any of these, if a core dev is reading?

Isn’t 1 already the case? Have you tried the new meteor create --bare? Also none of the core packages use lodash.

@sashko That’d be awesome if that were the case regarding Jquery. But I’ve never been able to get rid of it in a react app. I suppose it’s in a hard to find place from one of my meteor packages, if that’s the case.

Running “npm ls” doesn’t show any packages with jquery as a dependency.

I also ran this command to view Meteor package dependencies and still don’t see Jquery (results below). But this must not be going deep enough given Jquery is still included.
for p in meteor list | grep ‘[1]’ | awk ‘{ print $1"@"$2 }’; do echo "$p"; meteor show "$p" | grep -E '^ [a-z]'; echo; done

check@1.2.5
  ejson@1.0.13
  modules@0.7.9
  underscore@1.0.10

dynamic-import@0.1.0*
dynamic-import@0.1.0*: not found

easysearch:core@2.1.10
  check@1.2.4
  ecmascript@0.5.9
  erasaur:meteor-lodash@4.0.0 (weak dependency)
  isobuild:isopack-2@1.0.0
  mongo@1.1.14
  underscore@1.0.10

ecmascript@0.8.0
  babel-compiler@6.19.1
  babel-runtime@1.0.1
  ecmascript-runtime@0.4.1
  isobuild:compiler-plugin@1.0.0
  isobuild:isopack-2@1.0.0
  modules@0.9.0
  promise@0.8.9

flemay:less-autoprefixer@1.2.0
  caching-compiler@1.0.0
  ecmascript@0.1.5
  isobuild:compiler-plugin@1.0.0

gadicohen:sitemaps@0.0.26
  check@1.0.0
  gadicohen:robots-txt@0.0.9
  underscore@1.0.0
  webapp@1.0.2

kadira:flow-router-ssr@3.13.0
  ddp@1.2.3-rc.1
  ecmascript@0.4.1-rc.1
  ejson@1.0.9-rc.1
  isobuild:isopack-2@1.0.0
  meteorhacks:fast-render@2.14.0
  meteorhacks:inject-data@2.0.0
  meteorhacks:picker@1.0.3
  modules@0.5.1-rc.1
  reactive-dict@1.1.5-rc.1
  reactive-var@1.0.7-rc.1
  tracker@1.0.11-rc.1
  underscore@1.0.6-rc.1

meteor-base@1.1.0
  ddp@1.2.5
  dynamic-import@0.1.0
  hot-code-push@1.0.4
  livedata@1.0.18
  underscore@1.0.10
  webapp@1.3.16

mongo@1.1.18
  allow-deny@1.0.5
  autopublish@1.0.7 (weak dependency)
  binary-heap@1.0.10
  callback-hook@1.0.10
  check@1.2.5
  ddp@1.2.5
  diff-sequence@1.0.7
  disable-oplog@1.0.7 (weak dependency)
  ecmascript@0.8.0
  ejson@1.0.13
  facts@1.0.9 (weak dependency)
  insecure@1.0.7 (weak dependency)
  isobuild:isopack-2@1.0.0
  minimongo@1.2.0
  mongo-id@1.0.6
  npm-mongo@2.2.24
  random@1.0.10
  tracker@1.1.3
  underscore@1.0.10
  webapp@1.3.16 (weak dependency)

react-meteor-data@0.2.11
  ecmascript@0.4.1
  isobuild:isopack-2@1.0.0
  tmeasday:check-npm-versions@0.2.0
  tracker@1.0.11

reactive-var@1.0.11
  tracker@1.1.1

shell-server@0.2.3
  ecmascript@0.5.7
  isobuild:isopack-2@1.0.0

standard-minifier-css@1.3.4
  isobuild:minifier-plugin@1.0.0
  minifier-css@1.2.16

standard-minifier-js@2.1.0
  babel-compiler@6.19.1
  ecmascript@0.8.0
  isobuild:minifier-plugin@1.0.0
  minifier-js@2.1.0

static-html@1.2.2
  caching-html-compiler@1.1.2
  ecmascript@0.5.8
  isobuild:compiler-plugin@1.0.0
  templating-tools@1.1.2
  underscore@1.0.9

tracker@1.1.3

  1. a-z ↩︎

Other ideas:

  • Google’s Closure Compiler also does dead code elimination
  • Brotli &/or Zopfli compression for static assets

Not to be snarky, but this is akin to asking for Santa’s next ETA the day after Christmas.

11 Likes

lol a bit. Just starting a discussion.

Was hoping to see a couple of these in 1.5 (Jquery removal…but maybe that was done…TBD + tree shaking). JS bundle size and evaluation has been a big painpoint in my meteor app and I’ve been anxiously awaiting 1.5 and it didn’t decrease my app size at all. So I want to start a discussion about remaining ideas for this, understand the roadmap better, mention some things like PrePack.io which are new and developers may not come across yet, and hopefully get other ideas from community members…or snarky comments, whichever. :slight_smile:

1 Like

If bundle size is a pain point, I’m curious to know how you aren’t able to see any improvement from 1.5?

A lot of these things were on our mind, but we wanted to ship the current features ASAP! Tree shaking and more dependency removal and analysis is still on the table.

4 Likes

Awesome. I have huge respect for MDG’s work. It’s good reassurance that those items are still on the table!

I think prepack.io will eventually be a nice win too for decreasing JS evaluation time when it reaches production readiness. Something to keep eye on.

1 Like

Code splitting seems to be a biggest benefit to those who have apps which can be split based on different routes or app sections.

I have basically a SPA that can’t be split, it’s one page with MVP features, and anything that can be delayed is too small to not matter. But it has pretty strict speed goals based on the type of users that will use it (last minute type of thing). It’s not huge (300kb), but still ~2.4s to start render and 4.4s to visually complete on webpagetest.org. Goal is 2s - 2.5s to visually complete above fold. My non-SPA sites used to be 1.2s.

My two biggest offenders are Jquery (which I don’t use, but frustratingly can’t get rid of) and React currently. Considering switching from React (45kb) > Inferno (9kb) to get this down.

Speed is a discussion I enjoy. I still hope to hear more ideas here. Outside of meteor, speed has been an area of my specialty. I enjoy talking about this stuff and learning how to improve meteor apps.

2 Likes

I guess I would be investigating SSR then? Also, I would expect to see subsequent load times on your page drop if you use the 1.5 import model to cache your modules. Although that still wouldn’t help on the initial load…

1 Like

That is totally done/do-able. I have been running meteor without jquery for a while, but it’s hard to uncover all the places it can be. Try removing kadira.

I know MDG is working on removing underscore. Most of your client code could probably omit it if you’re using react and not blaze.

Also, bonus, if you import the dot notated individual packages like lodash.clone you can get by with only that one slice of lodash.

If you import lodash/clone you still get the whole bundle.


I’ve been working on a post about this topic, for anyone interested in it… not sure if my article is any better than the meteor blog and this thread, but at least it’s a new take and perhaps covers a slightly different angle:

Hope this helps, and let me know if I need to correct anything. I plan on making a few more posts from different angles.

6 Likes

Thank you! Good to hear confirmation that it’s possible to remove jquery. I think it’s a dep of a dep’s dep :slight_smile: Will have to keep searching.

Your blog post is fantastic! That will be really good for people to understand dynamic imports. I don’t notice any errors but I think it would be useful to explain why/why not to dynamically load react specifically. I wouldn’t because it is needed for the 1st load, but it’s something that I think would be useful for people. Very very good post!

Discovered a new Chrome extension from Google for speed, progressivity, and accessibility analysis: https://chrome.google.com/webstore/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk/related

1 Like

@benjamn @sashko One more speed idea: meteor could use HTTP2’s server push by including Meteor’s CSS and initial JS bundle in the http header. This would eliminate the wait for the HTML page and let CSS and JS download and be parsed at the same time:

Significant win for users on modern browsers.

Accomplished by adding a Link to the header:

Link: </asset/to/push.css>; rel=preload; as=style
Link: </asset/to/push.js>; rel=preload; as=script

More info: https://blog.cloudflare.com/announcing-support-for-http-2-server-push-2/

4 Likes

Are you absolutely certain about this? The lodash docs say importing via slash notation will reduce bundle size:

// Cherry-pick methods for smaller browserify/rollup/webpack bundles.
var at = require('lodash/at');
var curryN = require('lodash/fp/curryN');

If this is not the case, I would like to know!

1 Like

I’m also very curious because I also think importing from lodash/clone will result in a bundle size improvement, actually better than importing from lodash.clone.

Well - one of my other tests was off, so I suspect this could be too. I tried to make a micro test for it, but something was still importing all of lodash… so I’m going to make a super-micro focused test and I’ll let you know once and for all… (WIP)

1 Like

I published the new 07-profiling branch

@sashko @moberegger

4 Likes

importing from lodash/* should result in smaller bundle size.

There is a babel-plugin which rewrites your lodash imports so that it always uses cherry-picking:

however, I still found the whole lodash-lib in my bundle, so i am not sure if this works with meteor, because meteor also fumbles on the import-statements

1 Like