[Performance] Using this.$route inside a computed triggers reactivity while we are changing pages
RodrigoProjects opened this issue · comments
Environment
- Operating System: Darwin
- Node Version: v20.10.0
- Nuxt Version: 3.11.2
- CLI Version: 3.11.1
- Nitro Version: 2.9.6
- Package Manager: pnpm@8.13.1
- Builder: -
- User Config: ssr, experimental, sourcemap, app, hooks, devServer, modules, devtools, imports, vite, nitro, components, pinia, i18n, runtimeConfig
- Runtime Modules: @internal/cms-renderer@1.0.0, @internal/module-proxy@1.0.0, @nuxtjs/i18n@8.2.0, @nuxt/devtools@1.0.6
- Build Modules: -
Reproduction
https://stackblitz.com/edit/github-89dmst?file=pages%2F[...slug].vue
Describe the bug
Issue:
- When using options API with Vue and creating a computed that utilizes
this.$route
inside it, the computed will trigger when we click on aNuxtLink
while we are still changing pages. Being inside a computed seems special because if you inject$route
directly into the template it does not update until page change.
Observing the issue should be easy with the provided reproduction.
Notes:
- The same cannot be said when we use Composition API and the
useRoute
composable, if we create a computed that uses the value of the composable inside it will not trigger during a route change (correct behavior for me). - The first query object is a direct template injection of
$route
, the second one is a computed usingthis.$route
inside and you can see that it updates while changing pages, the third one is a computed using theuseRoute
composable value and it only updates after route change.
Impact:
- Currently our big E-Commerce website has some heavy computations associated with the
this.$route
while we are migrating to full Composition API, each page change it's triggering some computeds and, by consequence, patching a full component with a lot of data. - I got a workaround by having a proxy route computed that checks if the route the computed is re-calculating it's the same route the component was originally created at (not scalable for our use case).
Additional context
No response
Logs
No response
Start a new pull request in StackBlitz Codeflow.
The issue comes from vue-router and suspense - we need to ensure that within each suspense fork we have different route objects. We in fact even do custom handling within <template>
(injecting a custom route
object) and for useRoute
...
We probably need to port this fix to this.$route
as well, if possible.
Hello 👋, I started working on this issue, but I got a little stuck. I didn't manage to make it work, but from investigation it looks like this.$route
inside computed is not waiting for page:finish
hook and it gets the new route object right away.
@jakubednar This might help. It's how we ensure that we use the provided route rather than this.$route
:
nuxt/packages/nuxt/src/pages/plugins/route-injection.ts
Lines 17 to 27 in f209158
I could create a Vue plugin and overwrite the $route
with the Nuxt provided route. What you think of this as potential fix? @danielroe
Doing a replace like you did will be hard for this case when using options API
I tried fixing it yesterday, but all I managed to affect was useRoute
, $route
inside template. Maybe I'm doing something wrong, but I couldn't affect this.$route
inside script.
I will be grateful for any help with this.
I tried fixing it yesterday, but all I managed to affect was
useRoute
,$route
inside template. Maybe I'm doing something wrong, but I couldn't affectthis.$route
inside script. I will be grateful for any help with this.
After further investigation, I think you can do something like this:
`js
const INJECTION_RE = /\bthis.$route\b/g
if (!INJECTION_SINGLE_RE.test(code) || code.includes('ctx..provides[__nuxt_route_symbol')) { return }
let replaced = false
const s = new MagicString(code)
s.replace(INJECTION_RE, () => {
replaced = true
return '(this._.provides[__nuxt_route_symbol] || this.$route)'
})
if (replaced) {
s.prepend('import { PageRouteSymbol as __nuxt_route_symbol } from '#app/components/injections';\n')
} `
Did this very fast but I think you get the general idea, do a replace for this.$route
too and inject the "nuxt route" and fallback to this.$route like daniel did for the _ctx case!
I did something similar, but i got error thatthis
is undefined. But I did it with dynamic regex which supported both ctx
and this
cases. I'm going to try to handle just this.$route
case.
Thanks for making the PR for me @jakubednar time saver! <3