Open LiHDong opened 3 years ago
It seems like a Vue-router
bug.
It seems like a
Vue-router
bug.
I'm sorry that I cannot figure out where the problem is. Look forward to an avaible solution.Thanks!
@LiHDong moved to vue-router repo for the moment
The problem comes from the nested router-view inside UserCenter: because it's kept alive, it reacts to route changes and tries to render with the new nested view. I will see if there is a way to prevent this.
The problem is the same as https://github.com/vuejs/vue/issues/8819 which I don't know if it's expected or not. @yyx990803 is it normal for an inactive kept-alive component to keep rendering while inactive?
In the context of vue-router I tried internally avoiding rendering the router-view when the component is inactive, but it's too late, it still gets to mount the children once, resulting in mounting two Detail pages. So I tried not changing the route for nested router views but it turns out the onDeactivated
hook triggers after computed based on the current route location, not allowing me to pass the old version of a route
// getting the global route or a route injected by a parent router-view
const injectedRoute = inject(routerViewLocationKey, inject( routeLocationKey));
onDeactivated(() => {
console.log('deactivated', depth)
})
const myRoute = computed(() => {
console.log('computing myRoute')
return (props.route || unref(injectedRoute))
})
// providing the route to nested router-view
provide(routerViewLocationKey, myRoute)
This prints computing myRoute and then deactivated. If it was the other way around, I could have cached the previous value of myRoute.
So far I don't see a way to handle this a part from manually deactivating any critical watcher with a variable that is toggled inside onDeactivated()
edit: Opened issue on Vue Core looking for guidance tldr: in this example the page changes after the new component is rendered. That where the router is currently blocked to fix this
Also ran into the mounted issue in nested routes when using keep-alive, which causes repeated calls to the database :( Didn't find a solution :(
vue@3.0.5 vue-router@4.0.3
Same problem here since from the beta version and to the v4.0.5
my component named SubModuleA.vue, its activated deactivated twice
I also have a similar problem. Is there any solution.
I have also encountered this problem recently. Has this problem been solved?
We have also encountered this issue. This issue will cause the component-level KeepAlive to fail if we are using Vue Router 4.x.
Hello, Happy new year everyone !
This issue affects performance and kind of defeats the purpose of kept-alive router-views on vue 3. For example (extreme demo based on a real life observation):
Using keep alive: we see the double mount. Total time : 2.08s
Not using keep alive: single mount. Total time : 1.11s
We can see here that changing page is twice slower if router-view
is wrapped in keep-alive
tag.
Worth noting that using keep-alive
in vue 2 in the exact same scenario we get a total time of around 0.7s, 3x faster :(
(I can share a reproduction if needed)
It also seems that the activated
event is triggered on the component that gets deactivated
.
Reproduction here : https://github.com/nicolas-t/vue-3-keep-alive-lifecycle
Demo here : https://vue-3-keep-alive-lifecycle.netlify.app/home/nested
Related topic : https://forum.vuejs.org/t/vue-3-keep-alive-lifecycle-issue/125549
I know it's been more than a year since this issue has been opened, but is there anything we can do to help ? It's blocking me and maybe others to migrate from vue 2 to vue 3 at the moment.
Thanks :)
This is also true when using nested <Suspense>
rather than <KeepAlive>
: https://codesandbox.io/s/lively-mountain-o395nx. It feels like the cause is likely similar, but I'm happy to open a new issue if you think not.
Hi, I have a similar issue with two nested QRouteTab component (from Quasar). Reproduction link: https://codesandbox.io/s/without-qtabpanel-m43nec If you click ont "test B" tab, the setup of test j is executed twice.
Console output of navigation between tabs: Test A (test i) -> Test B (test j)
[Quasar] Running SPA.
##### setup -> index
##### setup -> layout test A
##### setup -> test i
##### setup -> layout test B
##### setup -> test j
##### setup -> test j
Also it seems that child component need to be mounted twice before to "keep-alive correctly". Example: if you comeback on test A the setup of "test i" is rerun. Then, you can navigate between both tabs without additional mount:
Console output of navigation between tabs: Test A (test i) -> test ii -> Test B (test j) -> Test A (test i) -> test ii -> test iii
[Quasar] Running SPA.
##### setup -> index
##### setup -> layout test A
##### setup -> test I # first time
##### setup -> test ii # first time
##### setup -> layout test B
##### setup -> test j
##### setup -> test j
##### setup -> test i # second time
##### setup -> test ii # second time
##### setup -> test iii
##### setup -> test iii
If you think this issue can be fixe with a cleaner code (good practice), I will thank you in advance for your help.
I made a minimal reproducible example using vue-router@4.0.15 here: https://codesandbox.io/s/gifted-gagarin-g3ugux?file=/src/main.js
It contains two child router-view, one uses keep-alive and the other one does not, and when navigating from /a/a
to /b/a
, the setup function in BA.vue logged twice.
In 2022, it seems the bug still exists. If keep-alive nested routes are used, the render is repeated
记录
vue-router 4.0.15
- 第二次时,调用 inject 会报:[Vue warn]: injection "xxx" not found.
- 进来页面先调用 onDeactivated ,再调用 onActivated
- 退出页面先调用 onDeactivated ,再调用 onActivated,后再调用 onDeactivated
Deadalusmask's repro code shows one RouterView
with a KeepAlive
and one without. It seems to me that even if there are no KeepAlive
s in the nested RouterView
s, the mounted
and created
hooks still fire twice in the child components: https://stackblitz.com/edit/vue-wvgvfa?file=src%2FApp.vue Navigate back and forth between Child1 and Child2 using the links and notice that created
, mounted
, and unmounted
fire multiple times. For example, here's the result of navigating from Child1 to Child2.
Child2 created
Child2 created
Child1 unmounted
Child2 mounted
Child1 unmounted
Child2 mounted
I might take a stab at fixing this issue. I'm not familiar with the Vue Router code, so any hints on where to start would be helpful. Also, the documentation is (IMO) not clear in regard to using KeepAlive
in RouterView
's slot. Specifically, it's not clear to me if child components should be persisted in cache when the top-most RouterView
uses KeepAlive
, or if nested RouterView
s should be required to use KeepAlive
. My assumption is that the latter case is correct, but this part of the documentation seems contradictory:
When a component instance is removed from the DOM but is part of a component tree cached by
, it goes into a deactivated state instead of being unmounted. When a component instance is inserted into the DOM as part of a cached tree, it is activated.
测试使用 vue-router 版本:4.0.12
@lin09 我也因为 inject 的问题找到这儿,发现多层 router-view 的情况下,如果有 keep-alive 保活,那么子组件 setup 中会执行多次,执行的次数取决于同级的 keep-alive 的数量
测试时在 vue-devtools 中可以看到预料之外的层级关系,组件出现在不该出现的位置,导致存在三份组件,所以 setup 中的代码执行了三次
这是我的路由表,其中 app.vue、index.vue、letter.vue、number.vue、car.vue 中的 router-view 都使用了 keep-alive
<router-view v-slot="{Component}">
<keep-alive>
<component :is="Component"/>
</keep-alive>
</router-view>
const routes = [
{
path: "/",
redirect: {
name: "index"
}
},
{
path: "/index",
name: "index",
component: () => import("/src/views/index.vue"),
redirect: {
name: "letter"
},
children: [
{
path: "letter",
name: "letter",
component: () => import("/src/views/letter/letter.vue"),
redirect: {
name: "letterA"
},
children: [
{
path: "letterA",
name: "letterA",
component: () => import("/src/views/letter/a.vue")
}
]
},
{
path: "number",
name: "number",
component: () => import("/src/views/number/number.vue"),
redirect: {
name: "numberOne"
},
children: [
{
path: "numberOne",
name: "numberOne",
component: () => import("/src/views/number/one.vue")
}
]
},
{
path: "car",
name: "car",
component: () => import("/src/views/car/car.vue"),
redirect: {
name: "carBmw"
},
children: [
{
path: "carBmw",
name: "carBmw",
component: () => import("/src/views/car/bmw.vue")
}
]
}
]
}
];
I use onActivated and onDeactivated to solve issue. Set a parameter isActive
using these hooks
<router-view v-if="isActive" />
Yes it is ugly and adds unnecessary complexity to the code.
is it possible to have this issue pinned?
Continue to nested Keepalive in Keepalive can fix this bug app is the first level ,amd、cmd is the second,Their routing methods are as follows https://codesandbox.io/s/modest-thompson-klqcel However, this will cause setup to execute multiple times
I had the same problem 🥲🥲🥲 https://github.com/vuejs/core/issues/7057
Hello, Happy new year everyone !
This issue affects performance and kind of defeats the purpose of kept-alive router-views on vue 3. For example (extreme demo based on a real life observation):
Using keep alive: we see the double mount. Total time : 2.08s
Not using keep alive: single mount. Total time : 1.11s
We can see here that changing page is twice slower if
router-view
is wrapped inkeep-alive
tag. Worth noting that usingkeep-alive
in vue 2 in the exact same scenario we get a total time of around 0.7s, 3x faster :( (I can share a reproduction if needed)It also seems that the
activated
event is triggered on the component that getsdeactivated
. Reproduction here : https://github.com/nicolas-t/vue-3-keep-alive-lifecycle Demo here : https://vue-3-keep-alive-lifecycle.netlify.app/home/nestedRelated topic : https://forum.vuejs.org/t/vue-3-keep-alive-lifecycle-issue/125549
I know it's been more than a year since this issue has been opened, but is there anything we can do to help ? It's blocking me and maybe others to migrate from vue 2 to vue 3 at the moment.
Thanks :)
It's been another year 😭
有好的临时解决方案吗?
尝试将多级嵌套路由打成平级
https://github.com/vbenjs/vue-vben-admin/issues/215#issuecomment-781920149
有好的临时解决方案吗?
可以试下命名视图
有好的临时解决方案吗?
尝试将多级嵌套路由打成平级 vbenjs/vue-vben-admin#215 (comment)
事实上我所需的功能就是需要多级路由,平铺的结构会带来很多其他问题,这还真是个远古bug
你可以在注册路由的时候打平结构就好了,其他地方还是保留原有的层级
有好的临时解决方案吗?
可以试下命名视图
这个命名视图是指什么
https://router.vuejs.org/zh/guide/essentials/named-views.html 看这个
有好的临时解决方案吗?
尝试将多级嵌套路由打成平级 vbenjs/vue-vben-admin#215 (comment)
事实上我所需的功能就是需要多级路由,平铺的结构会带来很多其他问题,这还真是个远古bug
你可以在注册路由的时候打平结构就好了,其他地方还是保留原有的层级
可能你没有理解我的意思,就是我的需求就是有三级路由,比如主 layout 下面有个页面有自己的 layout,这个layout 下有三级路由,而且也是通过 router-view 去渲染的。这个三级路由的 bug 是 setup 中的内容不会在重复进入时重复执行
或许x-255提供的方式可能会适合你
有好的临时解决方案吗?
有的
有好的临时解决方案吗?
有的
这是一个最小的复现demo:https://github.com/Miofly/vue-keep-alive-issue 应该如何去解决呢
有好的临时解决方案吗?
有的
这是一个最小的复现demo:https://github.com/Miofly/vue-keep-alive-issue 应该如何去解决呢
可以尝试一级 router-view 设置成:<component :is="Component" :key="undefined"/>
,或不设置 key
;
然后二级三级 router-view 设置成:<component :is="Component" :key="route.path"/>
。
Here's a hack I'm using to prevent the wrong child routes from mounting:
Setup some computed properties to get the child routes for the parent component
computed: {
routeNames() {
const parent = this.$router.options.routes.find(route => route.name === YOUR_PARENT_ROUTE);
const routes = parent && parent.children || [];
return routes.map(route => route.name);
}
}
Add a v-if to the Component
<RouterView v-slot="{ Component }">
<KeepAlive>
<Component
:is="Component"
v-if="routeNames.includes($route.name)"
/>
</KeepAlive>
</RouterView>
It's kinda nasty and fetching the $router options is not super recommended but it works 🤷
@posva @yyx990803 This issue has been around for two years now. I don't think this issue has such a low processing priority because essentially it's not a performance optimization, it's a logic error. ☹️
This hasn't advanced yet as noted in https://github.com/vuejs/router/issues/626#issuecomment-736355520.
It is so disappointing.This is really a logic issue and be opened for such a long time.
I created a custom keep-alive component as workaround fix to this bug:
vue3-keep-alive-component (https://github.com/emiyalee1005/vue3-keep-alive-component)
For anyone who encounter the issue can have a try on this
After some investigation, it seems this bug is down to a fairly complex interaction between KeepAlive and RouterView when using nested routes.
Also, the bug results in the child route component being mounted for EVERY RouterView instance in pages at that same depth, not necessarily just TWICE.
So, for example, if we have the following structure....
Page A -- Page A-A Page B -- Page B-A Page C --Page C-A
Performing the following steps in-order will produce these results:
This seems to be because each nested RouterView component is kept alive. This results in all nested RouterView components continuing to update even when they are deactivated. In and of itself this wouldn't be too bad, but unfortunately the RouterView component doesn't distinguish between which parent RouterView instantiated it, therefore in the above scenario, the RouterView components in "Page A", "Page B" and "Page C" all attempt to render all pages at their depth, e.g. "Page A-A", "Page B-A" and "Page C-A".
I have come up with a fairly solid hack which works around the above mentioned issue which I have include in the codesandbox as "FixedRouterView.vue".
The fix allows each nested RouterView to determine which section of the router config it is responsible for and ensures that it only renders page components from within that section.
https://codesandbox.io/s/brave-river-yq7r6v?file=/src/components/FixedRouterView.vue
To use the codesandbox demo:
Reproduce Issue Open the console Click through the pages at the top and see the duplicate console.log messages
With Fix Applied Reload the page Toggle the "Apply Fix" checkbox (which enables the FixedRouterView logic) Now again click through the pages at the top and you will not see any duplicate console.log messages
UPDATE: Ran into a bug in production when using the previous FixedRouterView component provided in the codesandbox. The issue centered around timing, navigating before nested routers had fully loaded resulted in the FixedRouterView "remembering" the wrong config.
I have now fixed the problem, dramatically simplified the code/approac. All of which is updated in the above linked codesandbox.
We have to do something very similar in Nuxt even without <KeepAlive>
. Suggestions or better implementations are very welcome. 🙏
@peteclark82's component is a very useful workaround. If anyone is looking for a TypeScript variant, it's below.
@posva Considering the comment from @peteclark82, do you still believe this is due to https://github.com/vuejs/vue/issues/8819?
@posva @yyx990803 This issue has been existing for three and a half years without any progress. It significantly affects multi-layout routing switching in mobile web applications and should not have been ignored for so long.
My issue mainly lies in the multiple triggers of onMounted
.
I’m sharing a rough approach here as a reference for discussion.
from: https://github.com/duanluan/wuyou-boot-ui/commit/37d2dd185d64f66b3718b8dd2bb7a574556eabd7
src/util/debounceLifecycle.ts
:
import {onMounted} from 'vue'
import * as CryptoJS from 'crypto-js';
const debounceMap: Map<string, number> = new Map();
const debounceExecution = (callback, delay = 300) => {
const key = CryptoJS.SHA256(callback.toString()).toString()
if (debounceMap.has(key)) {
clearTimeout(debounceMap.get(key))
debounceMap.delete(key)
}
const timeout = setTimeout(() => {
debounceMap.delete(key)
callback()
}, delay)
debounceMap.set(key, timeout)
}
const onDebounceMounted = (callback, delay = 300) => {
onMounted(() => {
debounceExecution(callback, delay)
})
}
export {onDebounceMounted}
src/view/sys/RolesView.vue
<script setup lang="ts">
import {onDebounceMounted} from "@/utils/debounceLifecycle.ts";
onDebounceMounted(async () => {
search()
})
<script>
Version
3.0.3
Reproduction link
https://codesandbox.io/s/nifty-roentgen-67uyr without vue router
Steps to reproduce
There is 5 files in the project. These files are App.vue, UserCenter/Index.vue, UserCenter/Push.vue, List/Index.vue, List/Detail.vue. Two Index.vue files are the child routes of App.vue, and I wrote keep-alive in App.vue. Push.vue and Detail.vue are child routes of two Index.vue, and I wrote keep-alive in two Index.vue to cache them.Here is the step to reproduce:
What is expected?
In step 3, I just expect it print once; In step4, it's not supposed to print again;
What is actually happening?
In these circumstance, I found the child route mount twice;And about step 4 in my project, I found the cache did function, but it did mount again, which was confusing.