The latter (using @cached
) is substantially cheaper, and it’s built on the Octane primitives, and therefore very much to be preferred. While @computed
is not going anywhere anytime especially soon, the long-term goal is to remove all of the old computed property mechanics from the framework and ecosystem: they are expensive, and complicated. Implementing your own cache is also reasonable at times, for example when you know more than the autotracking system about what to think of as cache keys. I would tend to reach for @cached
first, a custom cache second, and @computed
never. (We’re rapidly approaching a point on the LinkedIn.com app where we will be actively migrating all classic computed properties out of existence entirely and using linting to prevent the introduction of any new uses!)
As for why @cached
is cheaper: it’s precisely because it is built on the autotracking system. It will be re-executed only if tracked state consumed changes, for one thing. For another, it is guaranteed to be re-executed whenever tracked state it depends on changes: no chance of messing up the computed keys. It also can work through any number of transformations of whatever root state: no need to worry about things like the limitation of .[]
or .@each
. Finally, the actual caching mechanism itself is muuuuuuch cheaper: all it ever has to compare is one integer for every piece of tracked state it consumes, no matter how complex that tracked state is. By contrast, the cache comparisons when using @computed
can themselves be arbitrarily expensive!
As an aside, I would be careful to identify a concrete performance problem before reaching for even @cached
: caching even with autotracking still isn’t free and can sometimes still be more expensive in real-world effects on performance than just rerunning the computation. There are definitely places you want it, though, including if you’re deriving async state like a data load/API call dependent on the arguments to a component (as I covered here and here).