How to speed up Blaze rendering?

#1

One of my apps is still on Blaze, and I am trying to implement an endless scroll feature there. So, I initially get about 50 elements, then, once the user scrolls down, another 30 elements are being fetched and so on. This is done using a reactive variable.

I noticed that Blaze shows a quite inconsistent rendering behaviour. Most of the times, after setting the reactive var to a new set of elements, rendering is pretty fast (about 150ms). But sometimes, it takes ages (1-2s) until Blaze’s renderer actually reacts on the changed reactive var, by running the helper that actually retrieves the new value. This leads to a quite wonky user experience.

I am wondering what is causing these huge delays, and if there’s any way of speeding this up?

Another thing I noticed is that the helper gets twice on every update. I don’t see any reason for this, as the reactive var is only changed once, and the helper does not depend on any other reactive source. Any ideas what may cause this?

#2

How are your subscriptions setup? Are you subscribing multiple times (one subscription per page of data) or a single subscription that sends all data that changes for every page?

#3

I am not using any subscription here, only a Meteor method.

#4

Are you storing the data inside a reactive variable? That is your problem.

Blaze, or any front-end framework for that matter, cannot distinguish which objects inside the variable are updated, and so it must update the entire view from scratch.

Instead, try storing your data inside a local minimongo collection, and updating your data through there. It can be more efficient because documents are tracked by their _id.

You can initiate a local minimongo collection like this:

var cache = new Mongo.Collection(); // client only

For a further boost, you can filter in the fields that you want to display when returning the cursor to Blaze. That way, it doesn’t have to set up unnecessary Tracker instances.

3 Likes
#5

Thanks for the hint, good point. I did not use a local collection in this case, because the order of the elements in the endless scrolling list matters, but the elements themselves are retrieved randomly. I might add a kind of “order” attribute to them, though, maybe then the performance gets better. Still, the problem I am seeing is not the rendering time, it is the time until rendering actually occurs. I now saw that I can speed up rendering if I do a Tracker.flush() right after the elements have been retrieved. This somewhat eliminated the long delays, but the double-run of the helper is still happening.

#6

Same difference - IIRC, Blaze diffs against the DOM.

You can try sorting the local collection :slight_smile:

#7

Here’s another way to look at it. Whenever you update the reactive-var, you are essentially removing the old data and putting in new data. With minimongo, you can use the upsert function to only update the data where necessary. This means you would have less re-runs.

And thus, this is not a Blaze issue, but a data layer and reactivity issue, which is not really an issue :slight_smile:

2 Likes
#8

Could you post a CPU profile?

#9

On the helper getting called multiple times: are you updating the reactive-var in an autorun block? If so, what else are you updating in the same block? Having a lot of things going on in an autorun block leads to all kinds of things getting called numerous times. I’ve found success in breaking out bulky autorun blocks into separate distinct autorun blocks that each only manage one thing.

1 Like
#10

Only the ReactiveVar was changed.

#11

Using a local collection helped, indeed. Rendering times are much faster, and the helper is only called once (for the lifetime of the template, interestingly). Thanks!

1 Like
#12

Well, React does this way better using its explicit keys. But nevertheless, using a local collection helped.

#13

Network access will definitely take the longest time, but if you’re interested in further improvements, check out this article:

1 Like
#14

Thanks. In this article, you show code that uses Material Design’s ripple effect inside Blaze. Is there a package for this?

#15

I did it by adding a waves-effect class or doing Waves.ripple(node)

@Waves = require ‘node-waves’

require ‘node-waves/dist/waves.min.css’

1 Like
#16

Surely you see that you’re rediscovering publish and subscribe here?

#17

Well, maybe. If only there weren’t the heavy load issues on the server, then.

#18

Write a non-reactive publish:

Posts = new Mongo.Collection(...);

Meteor.publish("posts", function(page) {
  let self = this;
  Posts.find({page: page}).forEach(doc => { self.added("posts", doc._id, doc); });
  self.ready();
});

There won’t be a material difference in load between a method version and this version. But it will be far simpler this way!

1 Like