Pity attempts to make meteor faster look like they are ignored

Are you aware of SSR? It might be a good fit for what seems you are looking for.

2 Likes

Yes, I am. I have used SSR. The first load is super fast. But then transitions to other routes using react router is super slow when done from mobile devices. Again the app is not installed on the mobile device. So with SSR I got the initial load to be acceptable, but the rest of the app speed sucks. Have you managed to make transitions between routes fast on mobile devices when using SSR?

Extremely fast, far faster than the initial page load in virtually all cases. The one exception to this is destroying views with complex tables, where each cell is its own template. But apart from that, in general 0.3 second transitions or better. And that’s with quite a lot of data being loaded too.

Yes, I havent seen issues with loading other views after SSR. There are some views that might take a little longer because they have larger dependencies like a text editor, but has nothing to do with meteor. Actually I could just load that heavy component lazily and show a loading spinner or skeleton view until is ready. Just not requiered in this case.

Maybe you have large dependencies on your routes, or you are waiting for data before loading the views. Cant tell without more info.

Or maybe the devices or the connection is slow. Ive seen this happen but you can only address that by making your app ligther. Remove unused deps …

There are no huge apps in the world… If your page is more than 1MB let’s say … the concept is probably wrong. To that 1MB you start grabbing fully optimized assets and libraries. BUT … if your assets are some … GRID FS in your Mongo, or if your Mongo is half a planet far from your Node or if your server pipeline is 30Mbps because it runs on a nano box in Amazon, the app might look like a huuuge monolithic old tech with no support …

What does Chrome Network tab says about your loads? What is your mobile experience if you did it in Chrome (desktop) with the view set as let’s say an Iphone6?

What I noticed with React on mobile (I don’t use SSR), if you use Redux or any local data store and you load a component which has a lot of data in props (e.g. user wall of feeds), first the component will get ready and then the router will shift to the new route. I suggest you try to run your app ‘empty’ and see how it performs.
For me, on the first load (when I start pulling data from DB) of every page is lightning fast but then, it really depends on how much data I have in Redux (data used to display cards, user profiles etc). In React on mobile you really need to use virtualization or other technics for efficient painting if your DOM is “longer” than 2 screens and you should never load more than 2 screens on a first load. ‘2 screens’ does not sound too scientific but I think you know what I mean…
For the home page, if you use google analytics, or google maps or any global libraries, delay their start by as much as 3-5 seconds so that they load after the page load is complete.
Let me share with you an example of App.js I use to schedule assets or libs to I make sure they don’t affect the first paint or the first full page load:

class App extends Component {
  componentDidMount () {
    const { history, setShowIsometric, handleResizeWindow } = this.props

    handleResizeWindow({ width: window.innerWidth, height: window.innerHeight })
    window.addEventListener('resize', debounce(() => {
      handleResizeWindow({ width: window.innerWidth, height: window.innerHeight })
    }, 5000))

    setTimeout(() => {
      import('../../startup/client/libs')
      setShowIsometric()
    }, 400)

    setTimeout(() => {
      import('react-ga')
        .then(ReactGA => {
          ReactGA.initialize('...........')
          
          ReactGA.set({ page: window.location.pathname })
          ReactGA.pageview(window.location.pathname)

          history.listen((location, action) => {
            ReactGA.set({ page: location.pathname })
            ReactGA.pageview(location.pathname)
          })
        })
      import('react-redux-toastr/lib/css/react-redux-toastr.min.css')
    }, 5000)

    // TODO I might consider to only start maps if the user is logged in.
    setTimeout(() => {
      const GoogleMapsLoader = require('google-maps')
      GoogleMapsLoader.LIBRARIES = ['places']
      GoogleMapsLoader.KEY = '.........'
      GoogleMapsLoader.VERSION = '3.35'
      GoogleMapsLoader.load()
      GoogleMapsLoader.onLoad(google => {
        global.google = google
      })
    }, 6000)
    window.addEventListener('DOMContentLoaded', handlePrerender())
  }

Serving the Meteor bundle from a CDN is what I next consider to try. For some reasons all tests show an inefficient delivery of what is the biggest chunk of JS so I’d prefer to transfer that 2-3x more efficient … somehow.

With SSR, load a ‘template’ so that you have something to show and then … let the client pull data for it. I can assure you that even with complex queries (I use cultofcoders:grapher for things like posts of followers of friends) there are no performance issues. Node and React is fast enough for 2019. Chances that Meteor (Node) or React are slow are like 1 in a billion :slight_smile: … everything else is code and infrastructure design.

3 Likes

I hope waldgeist will take this comment constructively :). I am not an expert so this is just an opinion and I share this because I checked his activity, finding his comments in other posts.
On his Meteor web app, there is a screen with 2 buttons, a small menu and 2-3 icons. I view the page from Dubai, his server is probably in Germany. His page is really fast 1.65s to load, 2.45 to complete. But at the same time it is really slow (in terms of loss of opportunity) because half the time it is spent to get … hundreds of icons (fontawesome and material) that are not actually used on the home page and huge stylesheets (probably css libraries like the CSS part of fontawesome and material and bootstrap etc). For his page it doesn’t really matter because there are like 200 KB and this is just an example that I found relevant for this discussion, for more complex Apps, home pages (or the entry page if redirected) may be really slow if you don’t throw away everything that is not part of the home page.

3 Likes

This attempt (meteor-altboiler) is just an older version of SSR. Meteor (and the rest of JS community for that matter) are using SSR + CSR to manage the load time.

That should not be the case, this has to be handled on client side so it should be quick. Unless you either have really massive pages or blocking the rending due to fetching a large sum of data. In our case the transision with react router is extremely fast. Are you sure nothing is blocking the rending? with react, you really have to start lazy loading stuff when switching pages and be very mindful of the data fetching size by monitoring the network and socket console tabs.

1 Like

@alawi. Is there an example of using SSR + CSR for initial load and the CSR for moving from route to route thereafter? Please share.

basically every SSR example with react-router perform client side routing after hydration.

Most browsers nowadays provide excellent tools for profiling and analysing loading times and performance of web application. I advice to learn to use them.

The first load is super fast. But then transitions to other routes using react router is super slow when done from mobile devices.

Client side routing is just the same as re-rendering something based on state-change. it depends on what you do on this state change and which parts of your app rerender.

If this takes longer than the initial load, there is something horribly wrong. Profiling will help you there understanding the problem

2 Likes

Unfortunately I don’t have a public example but as @macrozone mentioned it’s bascailly any react SSR.

With that said, the chlallnge is in fetching and re-hydraing the data however NPDev package makes that process easier.

1 Like

Sorry for looking like I disappeared. I got caught up in something else. @alawi understood, thanks. @alawi/everyone, when I hydrate my data my page flickers - It renders from server and in an instant renders the same page from client, hence the page looks like it is flickering. Is this normal? I was expecting the hydration to be unnoticeable. Is this why you use NPDev package? My hydration is as per the meteor docs.

Here is a snippet of my server side code

onPageLoad((sink) => {  
  const context = {};

  const MyRoutes1 = props => (
	  <StaticRouter location={props.location} context={context}>
			if (sink.request.url.path == "/howItWorks")
				<Route exact path="/" render={() => <App2 page="HowItWorks"></App2>} />
	  </StaticRouter>
  );
  
  const MyRoutes2 = props => (
	  <StaticRouter location={props.location} context={context}>
			if (sink.request.url.path == "/registerInfo")
				<Route exact path="/" render={() => <App2 page="RegisterInfo"></App2>} />
	  </StaticRouter>
  );
  
  const MyRoutes = props => (
	  <StaticRouter location={props.location} context={context}>
			<Route  path="/" render={() => <App1 page="Index"></App1>} />
	  </StaticRouter>
  );
  
	if (sink.request.url.path == "/" ||  sink.request.url.path == "/home") {
		sink.renderIntoElementById('root', renderToString(<MyRoutes />));
	}
	else
	{
		if (sink.request.url.path == "/howItWorks")
		{
			sink.renderIntoElementById('root', renderToString(<MyRoutes1 />));
		}
		else
		{
			if (sink.request.url.path == "/registerInfo")
				sink.renderIntoElementById('root', renderToString(<MyRoutes2 />));
		}
	}
});

Only path="/" seems to be noticed on the server that is why I have MyRoutes, MyRoutes1 and MyRoutes2 all pointing to path="/" but with different page parameters, this seems to be a bug only I have; sorry I digress
And a snippet of my server side code

const MyRoutes = () => (  
	<BrowserRouter  >
	  <Switch>
			<Route exact path="/" render={() => <App1 page="Index"></App1>} />
			<Route exact path="/howItWorks" render={() => <App page="HowItWorks"></App>} />
			<Route exact path="/registerInfo" render={() => <App page="RegisterInfo"></App>} />
	  </Switch>
	</BrowserRouter>
);

onPageLoad(async sink => {
  ReactDOM.hydrate(
    <MyRoutes />,
    document.getElementById("root")
  );
});

Please note that I chose to use render={} instead of component={}, is this a problem? If you would like to see the imports I am using or anything else let me know. What am I doing wrong?

It should not flicker. The client should pick the page exactly where the server left it with the same data being passed to the components in both the client and server.

Flashing with SSR can be caused by not preloading all the necessary code and data. I use a lot of code splitting with Loadable, and rely on hydrated data. If I don’t have everything loaded before hydrating React, it’ll quickly flash to a “loading” state before all the assets/data are loaded. So in my hydration process, I have to make sure all that stuff is loaded before hydrating the react state.

Thanks for your input @captainn.

I looked around for a demo on implementing react ssr with react router to try and learn from an example and came across cult-of-coders/meteor-ssr-react-router-demo. I am however not able to clone it. When I run <git clone git@github.com:cult-of-coders/meteor-ssr-react-router-demo.git> I get Please make sure you have the correct access rights and the repository exists. If the repository exists can someone help on how to clone it. Is there another demo I can use? The cult-of-coders demo looks like a nice demo, it only has the bare essentials, it does not seem to include redux.

Use git clone https://github.com/cult-of-coders/meteor-ssr-react-router-demo

Thanks @cereal, and thanks to all who contributed. I’ll play round with the demo and see where I was going wrong.

1 Like

I also have an example in my starter https://github.com/CaptainN/meteor-react-starter

2 Likes

Thanks @captainn, I’ll check it out.

side note: why do you use the router in such a weird way? you can use the exact same code on server and client:

// Routes.jsx 
export default () => (
<Switch>
			<Route exact path="/" render={() => <App1 page="Index"></App1>} />
			<Route exact path="/howItWorks" render={() => <App page="HowItWorks"></App>} />
			<Route exact path="/registerInfo" render={() => <App page="RegisterInfo"></App>} />
	  </Switch>
)


// server.jsx
import Routes from '../common/Routes.jsx'
onPageLoad((sink) => {  
  const context = {};
  sink.renderIntoElementById('root', renderToString( <StaticRouter location={props.location} context={context}><Routes /></StaticRouter>));
})


// client.jsx
import Routes from '../common/Routes.jsx'

onPageLoad(async sink => {
  ReactDOM.hydrate(
    < BrowserRouter><Routes /></BrowserRouter>,
    document.getElementById("root")
  );
});

```
1 Like