Part 1 of this post I overview techniques I used and highlight the one that helped me beat the 5 seconds benchmark. In Part 2, I will share these techniques in detail, including the walk-through of some codes with a particular use case.
How I improved initial loading time for web apps built with Meteor — Part 2
I’ve used different tools to test performance. For the sake of simplicity for you to see results and to follow along, I refer here a free tool called Lighthouse owned by Google.
Lighthouse is an open-source, automated tool for improving the performance, quality, and correctness of your web apps. — Lighthouse App Description
Now let’s run some tests using Lighthouse. Here is how Medium.com performs according to Lighthouse.
We pay attention to two metrics, performance score and first meaningful paint. In this case, Medium gets a performance score of 36. It takes about 3.3 seconds for the primary content of the page to be visible.
Now let’s test meteor.com.
Themeteorchef.com — one of my favorite resources in working with Meteor.
Now it’s your turn to play around with Lighthouse 😉
Guess how much time it takes to pain a simple Meteor app — the one that you use
meteor create <my-app> to start? Before adding any code, try to find out the First Meaningful Paint using Lighthouse.
Hint: It takes around 9 seconds to paint something on the screen. After you add some codes and before any optimization, your Meteor app would easily go beyond the 12-second mark.
So am I out of my mind to set a goal of 5-second benchmark for a Meteor app when the simplest boilerplate app could not be able to beat the 9-second mark?
I’ve asked this question a lot before, especially when I read something like this.
A Bing study found that a 10ms increase in page load time costs the site $250K in revenue annually. — Rob Trace and David Walp, Senior Program Managers at Microsoft
More than 10 seconds of initial loading for a site just simply sucks!
Logically, I came up with a strategy in delivering a Meteor app, which is to serve a static landing page — which takes around 2 to 3 seconds to load, then only serve the core Meteor app with all the ‘bells and whistles’ to a target audience after.
But the strategy above is not the reason I am writing this post because everyone would think this way and many already have done it. No extra Medium post is needed, and obviously, it is NOT The solution you are looking for if you are still with me.
Logic will get you from A to B. Imagination will take you everywhere.
— Albert Einstein
By reasoning that my app only needs Meteor DDP only on very few occasions to serve some specific components, I imagine that there should be a way to ‘connect’ a very lightweight client app — a React frontend using Redux to manage client-side data, i.e. state. The app would get bundled and served using Webpack for high performance. It would be connected to a Meteor backend to leverage all the great works of the Meteor Development Group and their powerful real-time/data-on-the-wire technology.
Despite the challenge of solving the loading issue, I did not give up on Meteor like some other developers (they could not justify the performance cost of running Meteor app vs the benefits) is my experience of how Meteor have worked for small startups. I found out that startups with very few developers, Meteor would best suite for them to come up with a quick and dirty MPV to test out the concept.
Using Meteor, a lean startup team can develop and launch a Minimum Viable Product — MVP quickly to find out if there is a ‘product-market fit.’
Meteor real-time data sync capability allows me to do something like rendering UI components on demand. Rendering UI components on demand, I coil it intelligent user-interface or IU, is where the app renders different versions of a single component — i.e., a header that a startup uses to ‘pitch’ to early adopters about a product. Based on the data received, provided that if there is some acceptable level of statistical significance, the startup would ‘massage the message’ accordingly. Perhaps this topic should deserve another long post.
Before pushed codes to production, my MVP would dance around 12-second mark. Inline CSS helped a bit; using lazy loading trick helped a second here and there. It’s always a good practice to import only what I need, so I often reminded myself about this practice, i.e. import individual lodash module instead of the whole package for each component. etc. All of these tricks would increase performance to around 10-second. Less than that requires a HUGE amount of optimization time. Some occasions I found myself spent many hrs for less than half of a sec improvement. These time I would better spend talking to users.
Meteor community has said great thing about React Loadable. I found out that it saves me almost 2s in loading time by delay the loading of some ‘heavy’ components using react-loadable. In fact, I checked meteor.com and some other sites before the release of Meteor 1.5. The loading time before was more than 11 seconds. Now, many Meteor sites have reached the 10s benchmark. Probably some of the sites have upgraded their apps with react-loadable.
So what did I find out?
Use a DDP library like Asteroid to connect with a Meteor backend to stream data from a database and let React Redux Webpack take care the client side. Another word, don’t use Meteor for client side, otherwise it is impossible for your Meteor apps to load within 5-second.
Finally, here is the moment of truth!
For the testing app, I used React and Redux that connects with a Meteor backend using Asteroid. I hosted the Meteor backend using a free Heroku dyno and connected it with the React Redux frontend serving on localhost. I even added quite a few libraries that I often used for production apps plus an image as a background of the test page to mimic heavy content of a real app.
The 77 performance score plus less than 4 seconds of Initial Loading Time shows me that avoiding Meteor on the client side works!
Meteor has brilliant parts like the DDP, but because it does a lot of unnecessarily works on client side, it means more information needs to be downloaded before the first render can be performed.
I hope that by sharing my finding, I would be able to help you save some time finding a good solution for your Meteor apps. The end goal is to save our users some seconds of their lives in waiting for our apps to load.
Again, to avoid the post going too long, I am just publishing the first overview part of the finding now. I will go into details explaining the how in my next post.
In the second part of the story, I will walk through some codes and propose an architectural design pattern for a fast loading web app that can leverage real-time data sync of Meteor for a high performance admin dashboard or a customer service chat widget, of which I will be showing my working examples.
Here is part 2 of the story.