Trimming Server Code from Meteor Bundle
Trimming MB of unused minified JS code from the main bundle
After finishing up the feature set of Skoolers, it was time to take a look back to see how I could apply a little optimization to the site. The Meteor bundler takes care of minifying and packaging up your CSS and JS, so went to take a look at what it was putting together behind the scenes.
Big fat Meteor bundle
When someone lands on your Meteor page the first time, they need to grab the entire main bundle until they even begin rendering or subscribing to any data (this assumes you haven’t built any SSR). So you want to make sure that main bundle isn’t bloated. After browsing some forums on the subject, it looks like they tend to bloat pretty easily.
The first step to check the bundle was to use the handy new bundle visualizer tool released with Meteor 1.5 to check out what was being lumped into the main bundle. This is what it first looked like:
About 4MB of minified JavaScript, woah. Time to get dirty trimming some junk! Worst offenders in the bundle include:
- aws-sdk
- winston - don't ask me how it got in there, it's in a server only file
- useragent - only used in a few dev admin pages
- an entire SECOND copy of react and react-dom, included via a poorly maintained secondary dependency
Stop sending server only code to client
I was using the aws-sdk package to allow clients to upload various files to our S3 buckets. Since I want to control access to those buckets, when the client wanted to upload it would call a Meteor method that would generate a token using the aws-sdk package on the server which gets returned via the method.
The code to generate that token was only run on the server, enforced with a Meteor.isSimulation
condition, but since I was had import AWS from 'aws-sdk';
at the top, the bundler was including about 1MB worth of code in that main bundle that was never run.
To fix this I needed to add a conditional import that would make sure to not package up the code in the client bundle. The file ./server/lib
simply exported a default object which performed the function using the aws-sdk package that I needed to call.
let S3 = {};
if (Meteor.isServer) {
S3 = require('./server/lib').default;
}
Taking out aws-sdk and some other fat server-only deps shaved off a nice a few MB, but I still had some work to do, so I continued the bundle trimming by switching to using dynamic imports.