-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Prevent layout component re-rendering #38
Comments
For this effect, I'd recommend using layouts a little bit differently. Instead of defining them in your view components, you could define them as a meta property on your routes (potentially with a default to fall back on). Below is an example refactor that I believe should achieve what you want. 🙂 diff --git a/src/app.vue b/src/app.vue
index 6857411..b9702a8 100644
--- a/src/app.vue
+++ b/src/app.vue
@@ -9,6 +9,14 @@ export default {
return title ? `${title} | ${appConfig.title}` : appConfig.title
},
},
+ computed: {
+ LayoutComponent() {
+ return (
+ (this.$route.meta && this.$route.meta.layout) ||
+ require('@layouts/main').default
+ )
+ },
+ },
}
</script>
@@ -18,7 +26,22 @@ export default {
Even when routes use the same component, treat them
as distinct and create the component again.
-->
- <router-view :key="$route.fullPath"/>
+ <transition
+ name="fade"
+ mode="out-in"
+ >
+ <component
+ :is="LayoutComponent"
+ :key="LayoutComponent.name || LayoutComponent.__file"
+ >
+ <transition
+ name="fade"
+ mode="out-in"
+ >
+ <router-view :key="fullPath"/>
+ </transition>
+ </component>
+ </transition>
</div>
</template>
@@ -88,4 +111,17 @@ h6 {
#nprogress .bar {
background: $color-link-text;
}
+
+// ===
+// Transitions
+// ===
+
+.fade-enter-active,
+.fade-leave-active {
+ transition: opacity 0.5s;
+}
+.fade-enter,
+.fade-leave-to {
+ opacity: 0;
+}
</style>
diff --git a/src/router/views/404.vue b/src/router/views/404.vue
index 4ce46e9..1921d60 100644
--- a/src/router/views/404.vue
+++ b/src/router/views/404.vue
@@ -1,12 +1,9 @@
<script>
-import Layout from '@layouts/main'
-
export default {
page: {
title: '404',
meta: [{ name: 'description', content: '404' }],
},
- components: { Layout },
props: {
resource: {
type: String,
@@ -17,15 +14,13 @@ export default {
</script>
<template>
- <Layout>
- <h1 :class="$style.title">
- 404
- <span v-if="resource">
- {{ resource }}
- </span>
- Not Found
- </h1>
- </Layout>
+ <h1 :class="$style.title">
+ 404
+ <span v-if="resource">
+ {{ resource }}
+ </span>
+ Not Found
+ </h1>
</template>
<style lang="scss" module>
diff --git a/src/router/views/home.vue b/src/router/views/home.vue
index 540e538..dc71078 100644
--- a/src/router/views/home.vue
+++ b/src/router/views/home.vue
@@ -1,22 +1,20 @@
<script>
import appConfig from '@src/app.config'
-import Layout from '@layouts/main'
export default {
page: {
title: 'Home',
meta: [{ name: 'description', content: appConfig.description }],
},
- components: { Layout },
}
</script>
<template>
- <Layout>
+ <div>
<h1>Home Page</h1>
<img
src="@assets/images/logo.png"
alt="Logo"
>
- </Layout>
+ </div>
</template>
diff --git a/src/router/views/loading.vue b/src/router/views/loading.vue
index df01943..eaf6e8f 100644
--- a/src/router/views/loading.vue
+++ b/src/router/views/loading.vue
@@ -1,25 +1,20 @@
<script>
-import Layout from '@layouts/main'
-
export default {
page: {
title: 'Loading page...',
meta: [{ name: 'description', content: 'Loading page...' }],
},
- components: { Layout },
}
</script>
<template>
- <Layout>
- <transition appear>
- <BaseIcon
- :class="$style.loadingIcon"
- name="sync"
- spin
- />
- </transition>
- </Layout>
+ <transition appear>
+ <BaseIcon
+ :class="$style.loadingIcon"
+ name="sync"
+ spin
+ />
+ </transition>
</template>
<style lang="scss" module>
diff --git a/src/router/views/login.vue b/src/router/views/login.vue
index 7352feb..0f17226 100644
--- a/src/router/views/login.vue
+++ b/src/router/views/login.vue
@@ -1,5 +1,4 @@
<script>
-import Layout from '@layouts/main'
import { authMethods } from '@state/helpers'
import appConfig from '@src/app.config'
@@ -8,7 +7,6 @@ export default {
title: 'Log in',
meta: [{ name: 'description', content: `Log in to ${appConfig.title}` }],
},
- components: { Layout },
data() {
return {
username: '',
@@ -43,36 +41,34 @@ export default {
</script>
<template>
- <Layout>
- <form
- :class="$style.form"
- @submit.prevent="tryToLogIn"
+ <form
+ :class="$style.form"
+ @submit.prevent="tryToLogIn"
+ >
+ <BaseInput
+ v-model="username"
+ name="username"
+ />
+ <BaseInput
+ v-model="password"
+ name="password"
+ type="password"
+ />
+ <BaseButton
+ :disabled="tryingToLogIn"
+ type="submit"
>
- <BaseInput
- v-model="username"
- name="username"
+ <BaseIcon
+ v-if="tryingToLogIn"
+ name="sync"
+ spin
/>
- <BaseInput
- v-model="password"
- name="password"
- type="password"
- />
- <BaseButton
- :disabled="tryingToLogIn"
- type="submit"
- >
- <BaseIcon
- v-if="tryingToLogIn"
- name="sync"
- spin
- />
- <span v-else>Log in</span>
- </BaseButton>
- <p v-if="authError">
- There was an error logging in to your account.
- </p>
- </form>
- </Layout>
+ <span v-else>Log in</span>
+ </BaseButton>
+ <p v-if="authError">
+ There was an error logging in to your account.
+ </p>
+ </form>
</template>
<style lang="scss" module>
diff --git a/src/router/views/profile.vue b/src/router/views/profile.vue
index 3a500f3..ea57ae4 100644
--- a/src/router/views/profile.vue
+++ b/src/router/views/profile.vue
@@ -1,6 +1,4 @@
<script>
-import Layout from '@layouts/main'
-
export default {
page() {
return {
@@ -13,7 +11,6 @@ export default {
],
}
},
- components: { Layout },
props: {
user: {
type: Object,
@@ -24,12 +21,12 @@ export default {
</script>
<template>
- <Layout>
+ <div>
<h1>
<BaseIcon name="user"/>
{{ user.name }}
Profile
</h1>
<pre>{{ user }}</pre>
- </Layout>
+ </div>
</template>
diff --git a/src/router/views/timeout.vue b/src/router/views/timeout.vue
index 97a96d8..af443af 100644
--- a/src/router/views/timeout.vue
+++ b/src/router/views/timeout.vue
@@ -1,6 +1,4 @@
<script>
-import Layout from '@layouts/main'
-
export default {
page: {
title: 'Page timeout',
@@ -8,16 +6,13 @@ export default {
{ name: 'description', content: 'The page timed out while loading.' },
],
},
- components: { Layout },
}
</script>
<template>
- <Layout>
- <h1 :class="$style.title">
- The page timed out while loading
- </h1>
- </Layout>
+ <h1 :class="$style.title">
+ The page timed out while loading
+ </h1>
</template>
<style lang="scss" module> Let me know if that solves the problem for you. |
I'm going to assume this solves the problem, but happy to reopen if it doesn't. 🙂 |
It did indeed! Thanks, @chrisvfritz, you're the best. |
Hey @chrisvfritz, you did an awesome refactor here! I implemented this content in my project but I got a little problem, On the first load, it always show a little of the default layout until it gets totally loaded and goes to the desired layout specified in the meta, is there a way to solve this? I was thinking about making a "loading" kind of layout to be the default and set the main to everyone else, but I don't think it's the best solution :/ |
@marceloavf That idea with the loading layout sounds fine actually. 🙂 You'd just have to always define a layout. You could also simply not have a default layout, or only show the default layout after the component for the current route has finished downloading. |
Nice @chrisvfritz, I was thinking about this last idea, but I couldn't find a way to implement this. 😞 |
@marceloavf The easiest way might be to add something like a new router.currentRouteStatus = Vue.observable({
isLoaded: false,
})
router.beforeEach((routeTo, routeFrom, next) => {
router.currentRouteStatus.isLoaded = !routeTo.matched.some(
(route) =>
typeof route.components === 'function' ||
typeof route.components.default === 'function'
)
// ... Then update function lazyLoadView(AsyncView) {
const AsyncHandler = () => ({
component: AsyncView.then((componentConfig) => {
require('@router').default.currentRouteStatus.isLoaded = true
return componentConfig
}),
// ... And finally, inside the template of |
Works like charm @chrisvfritz, just a question, this code will not block this one from lazyLoadView? // A component to use while the component is loading.
loading: require('@views/_loading').default,
// Delay before showing the loading component.
// Default: 200 (milliseconds).
delay: 400, |
@marceloavf Great question. In order to prevent it from interfering, you'd have to render the |
@chrisvfritz thanks for the alternate way of using layouts above. I ran into a similar issue with the layout being re-rendered on route changes. For reference, the app I'm working on (tagnifi.com) has a filterable data table, and the selected filter params get persisted in the URL so the filtered search can be saved/shared. However every filter change causes the layout to re-render, so the form loses the current tab index, some temporary state on the page is lost, etc. I tried using the alternate approach you provided above, and while it resolves the above issue with route changes, I can't figure out now if/how I can use multiple named slots in my layout. The filter area appears in a specific spot of the layout using Anyhow if you might be able to point me in the right direction, it would be greatly appreciated. Thanks! |
@dnewkerk Hopefully you've solved the issue on your own by now, but you may want Also, I'm reopening this as I'm thinking about updating the routing strategy to something along these lines, as I've had a number of projects that have needed this kind of strategy and am thinking there might be more advantages than disadvantages for most projects. |
@chrisvfritz is there a branch that contains the code changes illustrated inside this comment? Thanks! |
@dnewkerk Did you ever find a solution to the multiple named slots issue?
Both solutions seem don't feel quite right for me, so I'm hoping you are willing to share what you came up with? |
I've been refactoring a work project using this repo as reference, and I've run into a snag. When navigating from one view to another, I'd like to avoid re-rendering the layout components that the views have in common. I'm after this for two reasons: one is efficiency, of course, but the more visible issue is that I'd like to transition the components that are entering and leaving.
For example, my "home" and "404" views use a layout that does not include a sidebar. The rest of my views use a second layout that does. When moving between "home" and another route, I'd like the sidebar to appear with a simple, satisfying transition; so, I wrap it in a
<transition appear>
tag and it works as anticipated. However, the sidebar also transitions out and in between routes that share the layout.Thinking it would solve the problem, I removed the
key
attribute from the<router-view>
in myapp.vue
, but to no effect.I know there's not much in particular to be done without looking at the source (which I am not permitted to share), but can you think of anything off the top of your head that I should check? Something basic I might have overlooked?
The text was updated successfully, but these errors were encountered: