'How to prevent double invoking of the hook created?
So I have a problem with my vue3 project. The gist: I need to support different layouts for some use cases: authorization, user profile's layout, group's layout, etc. I've got the opportunity by this way:
- Create a component
AppLayout.vue
for managing layouts
<template>
<component :is="layout">
<slot />
</component>
</template>
<script>
import AppLayoutDefault from "@/layouts/EmptyLayout";
import { shallowRef, watch } from "vue";
import { useRoute } from "vue-router";
export default {
name: "AppLayout",
setup() {
const layout = shallowRef(AppLayoutDefault);
const route = useRoute();
watch(
() => route.meta,
async (meta) => {
try {
const component =
await require(`@/layouts/${meta.layout}.vue`);
layout.value = component?.default || AppLayoutDefault;
} catch (e) {
layout.value = AppLayoutDefault;
}
}
);
return { layout };
},
};
</script>
- So my
App.vue
started to look so
<template>
<AppLayout>
<router-view />
</AppLayout>
</template>
- To render a specific layout, I've added to router's
index.js
special tagmeta
{
path: '/login',
name: 'LoginPage',
component: () => import('../views/auth/LoginPage.vue')
},
{
path: '/me',
name: 'MePage',
component: () => import('../views/user/MePage.vue'),
meta: {
layout: 'ProfileLayout'
},
},
- Now I can create some layouts. For example, I've made
ProfileLayout.vue
with some nested components: Header and Footer. I use slots to render dynamic page content.
<template>
<div>
<div class="container">
<Header />
<slot />
<Footer />
</div>
</div>
</template>
So, when I type the URL http://example.com/profile, I see the content of Profile page based on ProfileLayout. And here the problem is: Profile page invokes hooks twice.
I put console.log()
into created() hook and I see the following
That's problem because I have some requests inside of hooks, and they execute twice too. I'm a newbie in vuejs and I don't understand deeply how vue renders components. I suggest that someting inside of the code invokes re-rendering and Profile Page creates again. How to prevent it?
Solution 1:[1]
Your profile page loaded twice because it's literally... have to load twice. This is the render flow, not accurate but for you to get the idea:
- Your layout.value=AppDefaultLayout. The dynamic component
<component :is="layout">
will render it first since meta.layout is undefined on initial. ProfilePage was also rendered at this point. - meta.layout now had value & watcher made the change to layout.value =>
<component :is="layout">
re-render 2nd times, also for ProfilePage
So to resolve this problem I simply remove the default value, the dynamic component is no longer need to render default layout. If it has no value so it should not render anything.
<keep-alive>
<component :is="layout">
<slot />
</component>
</keep-alive>
import { markRaw, shallowRef, watch } from "vue";
import { useRoute } from "vue-router";
export default {
name: "AppLayout",
setup() {
console.debug("Loaded DynamicLayout");
const layout = shallowRef()
const route = useRoute()
const getLayout = async (lyt) => {
const c = await import(`@/components/${lyt}.vue`);
return c.default;
};
watch(
() => route.meta,
async (meta) => {
console.log('...', meta.layout);
try {
layout.value = markRaw(await getLayout(meta.layout));
} catch (e) {
console.warn('%c Use AppLayoutDefault instead.\n', 'color: darkturquoise', e);
layout.value = markRaw(await getLayout('EmptyLayout'));
}
}
);
return { layout }
},
};
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | 8bu |