-
-
Notifications
You must be signed in to change notification settings - Fork 8.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(reactivity): more efficient reactivity system #5912
Conversation
computed()
on-demandcomputed
recalculate on-demand
I think this may cause problems in cases that rely on the laziness of |
@skirtles-code Thanks for reviewing it, To avoid the bad architecture with circular dependencies, users should also extract the dependencies of |
e106183
to
91c0899
Compare
/ecosystem-ci run |
This comment was marked as outdated.
This comment was marked as outdated.
91c0899
to
fc84803
Compare
I'm not sure I understand the relevance of your first example. It seems you've added a circular dependency into my example. That shouldn't be there, it makes no sense. I don't think this can be dismissed as bad architecture. The example I provided is contrived, but I was just trying to illustrate the problem. It obviously looks ridiculous when presented in this form, but that doesn't mean it wouldn't be perfectly natural to see something like this in a real application. The check on The underlying problem here is that, in some cases, computed properties have now switched from being lazily evaluated to eagerly evaluated. It isn't just the potential for errors. The extra evaluation of When evaluating |
Not really, if you find that PR causing this problem, can you provide minimal reproduction or contribute a test case? |
That is what my example shows. That is why it causes an error on this branch but not on |
It's not a trade-off, just a bug, thanks for report that. |
Fixed by |
/ecosystem-ci run |
📝 Ran ecosystem CI: Open
|
9435daf
to
f3a0071
Compare
@ferferga You need to cache and return the old object if possible. let computed1Value
const count = ref(0)
const computed1 = computed(() => {
const prop1 = Math.round(count.value / 1000)
const prop2 = Math.round(count.value / 1000) * 2
if (computed1Value && computed1Value.prop1 === prop1 && computed1Value.prop2 === prop2) {
return computed1Value
}
return computed1Value = { prop1, prop2 }
}) |
@ferferga Didn't understand your question at first, but I do now and created a reproduction with both the version with and without cache side by side, so others can see the difference too 😄. Interestingly, this technique also prevents rerenders in Vue 3.3.x, so I learned something new today! |
@johnsoncodehk I played around with your technique a little more and was thinking: can't this be built into |
@Anoesj Still, this PR reduces a lot of rerenders in my project too. I just thought it also covered objects, but I understand that's a more complex topic. I'm not sure how that could be solved for all edge cases. Obviously pulling lodash is not a solution for Vue, so everything should be in-house. A solution must be found that is efficient enough for everybody too. I also wonder whether something like object-hash is more efficient in this case. |
Of course, I get that. It was just a quick PoC. But having some built-in way to prevent rerenders on non-primitive computeds would be very nice. |
@johnsoncodehk What are the difficulties you see to have it in core? It's great that we can finally have a one-size-fits-all approach to computed and no longer need computedEager, so I think it'd be great too to not have other comp's implementation I understand your concerns of more potential breakage, but maybe for 3.5? PS: Another simpler approach maybe is to |
@johnsoncodehk Nice, that would also work. Also, one hour ago? Dude, you're beast, haha! |
@ferferga Equality based on stringify wouldn't be optimal when objects have the same properties and values, but in a different order. When working with Array, Map and Set it makes sense to not count something as "equal" when the order is different, but with objects, in my opinion, it's not. Don't know how object-hash handles order though? Maybe, if a non-primitive equality check functionality is made available, there needs to be an opt-in setting to ignore order? Also curious how two class instances with the exact same properties, but a completely different prototype, should be handled. |
@johnsoncodehk
In vue 3.3 the computed, watch, and render are triggered and the array is changed. |
@beskar-developer. This PR was merged a long time ago. If you think you've found a bug then it'd be better to open a new issue at https://github.com/vuejs/core/issues. If you're unsure whether it's a bug you could also discuss it at either https://github.com/vuejs/core/discussions or on Discord first. In my opinion, this is neither a bug nor a breaking change, but I'd prefer not to go into details on this PR. |
This computed sorts someArray in place, which means it always returns the same reference. Since 3.4 computeds have become lazy. If the result is equal (as in this case, given it is the same reference) it will not trigger deps. Solution may be to return a new array when using the sort function, using |
Thanks but what I meant was this could be a breaking change, some bug, or maybe nothing! |
fix #311, fix #1811, fix #6018, fix #7160, fix #8714, fix #9149, fix #9419, fix #9464
Changes
computed
new value does not change,computed
,effect
,watch
,watchEffect
,render
dependencies will not be triggeredcomputed
only triggereffect
, syncwatch
, syncwatchEffect
onceshift
,unshift
,splice(
only triggereffect
, syncwatch
, syncwatchEffect
oncedeferredComputed
is now a re-export ofcomputed
and marked as deprecatedpauseScheduling
,resetScheduling
APIsDetail
computed()
Before result:
PR result:
effect()
/watch()
/watchEffect()
Before result:
PR result:
render()
For the following code, the template renderer is optimized to trigger once per second instead of triggering 100 times per second.
test cases:
Array
shift()
Before output:
PR output:
pauseScheduling()
,resetScheduling()
Wrap refs mutations with
pauseScheduling()
andresetScheduling()
to trigger side effects only once.