Skip to content

Computed properties can have widely different performance characteristics on client and server (because they are not cached during SSR) #10151

Open
@MoritzKn

Description

@MoritzKn

Version

v2.4.3 - current (v2.6.10)

Reproduction link

https://jsfiddle.net/anc6Lf23/2/

Steps to reproduce

  1. Open the JSFiddle
  2. You will see the time it takes to compute a value without caching of computed properties (~1 second) in the output
  3. Set "CACHE = true" in the first line of the JS part to see the time it takes to compute the value with caching (~2 ms)

This issue concerns SSR but for simplicity I created the fiddle which emulates the behavior of the server render:

  • CACHE = true – the behavior we usually have in the client
  • CACHE = false – the behavior during SSR

What is expected?

I would expect computed properties to have comparable performance characteristics on the server and client, so that I don't need to write custom code.
I.e. I would expect computed properties to be cached during SSR.

What is actually happening?

Computed properties are not cached and therefore have drastically different performance characteristics in some cases.


Description if the issue

Since computed properties are not cached during SSR some components unexpectedly take significantly longer to render. This is the case if it is heavy to compute the property or if it is accessed a lot.

I would usually expect a computed property to have constant time complexity (O(1)) no matter how often we access it. But on the server it suddenly becomes linear time complexity (O(n)). This is especially critical when the computed is accessed in a loop. When we have multiple computed properties relaying on each other, each containing loops, this gets really bad. Then it has polynomial time with the exponent being the amount of nested computed properties (E.g. O(n^3) for three levels of computes, like in the JSFiddle)

Real world example

I noticed this issue because our server renderer suddenly took multiple seconds (5-8 seconds) to respond after enabling a new component for SSR. Normally rendering the app on the server takes about 100ms.

The effected component is part of a proprietary code base but it is similar to the one in the JSFiddle. You can see the component in production here:

After finding out about this I also investigated other occurrences of this:
In our Vuex store we did not have any nested getters with loops, like described above, however some of the getters are somewhat heavy to compute (~1ms) and accessed a lot in various templates. So I decided to introduce a very simple custom caching layer to our store. This sped up server rendering by about 20%.

This could also be a low hanging fruit for optimizing SSR performance:
Based on analyzing our own app, I would roughly estimate that caching all computed properties could speed up server rendering by about 30% in an average Vue.js app.

For me this issue was hard to understand because the affected code seemed harmless at first.

Mitigation

To mitigate this issue you can move access to computes out of loops: access them once, store them in a local variable and then use this variable in the loop. This is generally a good idea since any look up of a property on a Vue.js VM has a small cost.

However this is not possible if you have a loop inside your templates.

References

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions