Meteor Nested Imports and React Router?

I was listening to @stolinski 's podcast ep titled “Meteor’s 2nd Life”. @filipenevola was interviewed and talked about a lesser-known Meteor feature called nested imports. He mentioned, if I understand correctly, that if a package is loaded using nested imports, it will be included in the app, but the code won’t be evaluated until needed.

My app is getting a 98 in performance in LIghthouse for desktop, but only an 80 for mobile - and on mobile Lighthouse says one of the biggest improvements I can make is to “reduce main thread work” with 3,296 ms being spent on “script evaluation”. So nested imports may be just what I need.

Now the question for a Meteor/React app is, how to implement them with React and React Router?

In the Router I currently do this, e.g.:

const ConditionsSearch = React.lazy(() => import("./conditions_search"));

[.....]

<Route exact path="/condition-search" render={(routeProps) =>
	<ConditionsSearch history={routeProps.history} match={routeProps.match}
	/>
}/>

I guess maybe I could have an AppState field called “mainAppHasLoaded” that gets set true via a useEffect when the app has loaded, and then have something like this in the router:

const ConditionsSearch = AppState.mainAppHasLoaded ? import("./conditions_search")) : null;

Would that be a possible approach? I’ll check it tomorrow (it’s after 2am here at the moment) and see if it compiles.

1 Like

Hi @vikr00001,

first of all please note what is coming in React Router: https://github.com/ReactTraining/react-router/blob/dev/docs/advanced-guides/migrating-5-to-6.md

Ideally you should update all your routes to be/call functions (with hooks) instead of React components.

I use this NPM: @loadable/component

import React from 'react'
import loadable from '@loadable/component'
const ConditionsSearch = loadable(() => import("./conditions_search"))
...
<Route exact path='/condition-search' component={Businesses} />

Your history would ideally be connected at the top router level (https://reactrouter.com/web/api/Router). When you create its instance you can export it and import it wherever you need in case it bothers you to add it as prop at each and every route.

With split code your bundle only includes the basic Meteor project, your router but no page(route) other than what you actually call.

Example: you load www.test.com and your main page is Home.js. You only load Home.js along with your bundle (2 separate transactions). None of the other routes are being loaded. You can see that clearly in your Network tab in Chrome. Each and every route you load afterwards is being cached in the browser so that the next time you go to www.test.com you only get the bundle, the Home.js is already on your local storage.
Check this image below. That is a dynamically loaded component with all its dependencies including CSS and other libraries.

1 Like

Thanks for this great info, @paulishca. For information purposes, is React Router 6 required for this approach to help in reducing script evaluation times on app launch?

Update 9:20pm Okay I tried it with React Router 5, and my Lighthouse score for mobile was unchanged. It looks like it’s time to update to React Router 6. :slight_smile:

Update 9:25pm Unless… I’m optimizing the wrong thing. Maybe my imports aren’t the thing that’s taking up all that script evaluation time. Hmmmmm…

RR6 won’t make a difference. Splitting code (dynamic imports) does. I only mentioned it as a heads up for your general project structure. Ok, with Lighthouse there a multiple things involved.
If you are done with splitting your code correctly and you can see those separate fetches you may consider an audit other than Lighthouse that gives more introspection.

Try this for instance: https://www.dareboost.com/ or this: https://gtmetrix.com/ or this:https://tools.pingdom.com/ - select the location from which you want to run - select one close and one very far and see whether the result changes.

Also, you need to see if tree shaking is done properly and you don’t have libraries that you don’t even use or fragments of CSS in JS etc. A good and strict sanitization is necessary for any project. For things like google maps, players etc, don’t use NPM. Once you need a map, there are ways to pull the map library (as a script tag) in your HTML head and then mount it. For a long time we used JIT (just in time), but there are many things that best work OWYNI (only when you need it) – I made this up now :).
If you use Lodash - drop it and only add Lodash NPM that you need (e.g. ‘lodash.pick’). If you use moments for dates, you can drop it in favor of dayjs… etc.

Use Meteor’s bundle visualisation. Up to 1MB bundle should be absolutely fine.
All /public assets should stay in a CDN.
Make sure your response time (from your Meteor server or the HTML cache provider) is really really tiny. In my main project I respond with an HTML page in 5-15ms.

It may be that Lighthouse audits your JS bundle instead of your cached HTML. Do you use Prerender or SSR? I favor Prerender (own install) because I have a lot of control over removing tags, assets etc and deliver highly efficient HTML pages to search engines (including Lighthouse). Looking back now to how I did things in the past, I see absolutely no reason not to get at least 95% on both mobile and desktop.

With Prerender I don’t even prerender mobile pages (different JS). I deliver desktop for the mobile test and still get 99%. However, for the browser, I use split code to deliver components based on screen width.

Thanks for the info on verifying dynamic loading via Network->Fetch->Preview. I’ve confirmed that the dynamic imports are working.

I’ve run my home page past the 3 test sites you noted, and they all seem to be saying everything looks good. Page size for the home page is about 600k.

I’m using Prerender on Galaxy via the Atmosphere mdg:seo package.

Here’s my Lighthouse score for mobile:

…and here’s the advice from Lighthouse:

Looking at Chrome devtools->Coverage, I see this:

43% unused bytes! I think this may be because the home page fetches / builds some components with auth dialogs and functions, plus some of the hamburger menu items, and they aren’t needed / used right away on initial page load.

I’m going to see if I can lazy-load them after the initial home page load…

It looks like the unused code is mostly React code that isn’t called for purposes of the home page.

Double-clicking the file in Chrome devtools->Coverage brings up the file and shows-line by line whether the code has been executed, with red meaning the code has not been used yet.

I guess I’m going to need SSR to improve this. :slight_smile:

at the top of the Chrome debug window, where you run the audit, you have two checkmarks: Clear storage and Simulate throttling. Could you please check Clear storage and run the test for mobile again. I am getting very different results with and without checked, when checked the score is similar to Google Page Speed Test.
I don’t see any good reason to change Prerender to SSR. I am convinced it won’t affect your score. In theory, nothing can be faster or more efficient and available than a Prerender running on a Redis cache.

With Clear storage checked, I’m getting 100 on both mobile and desktop!

MOBILE

DESKTOP