As my app gets larger I’m starting to see brutally long initial page load times, upwards of 15 seconds on a mobile browser. This is obviously unacceptable. I’m looking into ways to decrease this load time.
The two ways I can think of are:
- Code splitting
- Server-side rendering
I spent a bit of time yesterday trying both.
Code splitting - I tried migrating my app to
webpack using the
webpack:webpack package. I spent around 5 hours and ran into many issues building and have basically given up on this route.
webpack is very complex and I think it’s going to be a nightmare to maintain.
Server-side rendering - I installed
reactrouter:react-router-ssr and got it up and running fairly easily. I’m running into a pretty big issue with it though.
SCSS / CSS that’s imported into a JS file in Meteor gets appended to the head when the component loads. This doesn’t work for SSR. My app now renders super quickly, however there’s no styles there until the JS loads on the client which completely defeats the purpose.
Has anyone been able to get around this issue?
I think you try to solve the issue the wrong way round. I’m pretty sure it’s not because of the size of the application code if you can fix speed by adding SSR.
- Check network latency times, look for huge assets, packages, whatever things that maybe slowing down initial page load.
- Use Meteor Toys and Kadira to find bottlenecks, check your active subscriptions, rate, oplog (maybe too much data over DDP because of global publications with no limit, sort…)
- Profile your App with Chrome on the client-side (see https://kadira.io/platform/kadira-debug/cpu-profiling/taking-a-client-profile)
Btw: If the initial load takes longer on a mobile device vs pc connected to the same wifi, memory and ddp traffic is pretty much what I think is the issue here.
The whole point of SSR is that it doesn’t matter how big the bundle is. The rendered HTML will land on the client before any of the JS has loaded. That’s why it’s so fast.
It takes longer on a mobile device because:
- Mobile devices have slower internet, so downloading the bundle is slower.
- Mobile devices have slower processors so it takes longer to evaluate and run the JS before rendering the app.
What I really need is code-splitting, and I really hope there’s a clean Meteor solution to that problem that comes out soon. Until then, SSR might be able to be a stop-gap for the issues I’m experiencing if I can solve the CSS loading issue I described in my initial post.
Mobile devices have slower processors so it takes longer to evaluate and run the JS before rendering the app.
That is exactly the point, so you won’t solve it with ssr because the js needs to be evaluated regardless of having the page üre rendered which will take 15s. Big meteor applications get away with 5mb of whole bundle size without codesplitting and 2.5 mb each slice if code is splitted. With code splitting you can bring the bundle size down but maybe from 15 to 10 seconds. So look at your code and ddp there must be sonethibg fishy there.
Put it like that:
Even by using ssr your js rendering code has to be fast for further use after the initial page load.
Even if you use code splitting with webpack, there’s a whole bunch of core packages that needs to be bundled with each slice because of dependencies.
My app loads in 5s on 3g. 1.2mb of own code lines (compressed), bundle size is 3.5mb. No ssr, no webpack bundled
I don’t think you fully understand how SSR works or the issue that I’m facing.
- The entire point of SSR is that the SERVER renders the first page. It doesn’t matter how fast or slow the device receiving it is. The very first chunk of code sent to the device is the already rendered HTML and it displays it immediately. That why you get such a fast initial load time with SSR. The JS bundle doesn’t load until AFTER The page is already being displayed.
- My JS code is fast. Once the page has initially loaded, everything is very snappy. My code is great, my subscriptions are optimized, and I don’t have any issue with anything loading after the initial render. I also experience this issue on every page,
even pages that don't have ANY subscriptions or data whatsoever, such as my landing page, which shows me pretty clearly that data subscriptions are not the issue.
- Code-splitting absolutely would help a ton. Yes, there is some common code that would need to be sent down no matter what, but the initial bundle would be at most 50% of my current bundle, and that would make a MASSIVE difference of decreasing load time by at lease half. Going from 15s to 7s would be a gigantic win for us.
I appreciate that you’re trying to help but I don’t think you really understand the issue at hand and your suggestions have not been helpful. You’re essentially telling me I don’t know what I’m doing when it’s clear you don’t have a strong understanding of server-side rendering or code-splitting.
To solve the CSS issue I only know how to solve it with Flowrouter SRR. But I think
react-router-ssr uses the same logic than Flowrouter SSR. You have to add the style string to the head of
ssrContext. See here.
In webpack you can do something like this on the server to obtain the style:
require('!css!../css/TodoItem.import.css'). But I’m not sure if this is possible with native meteor. In general the kickstart-meteor-react-flowrouter-SSR-codeSplit repo is a good place to start.
Unfortunately what you’re suggesting requires Webpack. I was hoping to stay within the Meteor ecosystem. I tried using the
webpack:webpack package but was unable to get the app building after about a day of work.
I ended up completely migrating to Webpack. I’m no longer using Meteor as there were simply too many limitations.