Nuxt.js: Mounted twice when layout change

Created on 11 May 2019  ·  55Comments  ·  Source: nuxt/nuxt.js

Version

v2.6.2

Reproduction link

https://codesandbox.io/s/wn7j570y85

Steps to reproduce

Simply click on button named "Mounted triggered twice, see console" and see in console.
(reproducable only with css:false for transition)

What is expected ?

mounted once

What is actually happening?

mounted twice

Additional comments?

The bug is still present on last release 2.7.1
It can be fixed quickly ? it's really a brake on client-side performance.
(Related : https://github.com/nuxt/nuxt.js/issues/5616)

This bug report is available on Nuxt community (#c9188)
bug-confirmed bug-report

Most helpful comment

I found the issue, apparently the name of the layout is not defined, which cause a different name between server-side rendering and client-side (Default on SSR and layouts/default.vue on client).

As a quick workaround: Set a name for each of your layouts:

layouts/default.vue

<template>...</template>

<script>
export default {
  name: 'default'
}
</script>

I will make a PR to fix it in the next patch release of NuxtJS.

Thank you very much for reporting this and sorry for this outstanding delay 😞

All 55 comments

image

I pulled this down locally and am only seeing a single mount. Maybe this is an issue with code sand box?

no, you forgot something. The next page must have an other layout and css:false for transition (navigation on client side). I have the same bug locally.
I have to make a video to prove it to you?

Someone is alive ? :)

Hi @usb248
This still happens in version 2.8.0
The problem is when transition: {css: false} it's defined

Any core team feels concerned ?

It's not just transition, still mounts twice on this.$router.push

This still seems to be an issue when transitioning from one layout to another, could we get a re-open?

nuxt version 2.9.2

Happening with Nuxt version 2.10.0 as well. I am using nuxt generate. It only happens in the production build.

@amritk @swapneelvivekdesaithoughtworks with the same reproduction from above? If not, please provide one.

Hit the link and watch the console
https://codesandbox.io/s/nuxt-vuetify-k7yv9
2019-10-08:11:54:31

Thanks for your contribution to Nuxt.js!
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
If you would like this issue to remain open:

  1. Verify that you can still reproduce the issue in the latest version of nuxt-edge
  2. Comment the steps to reproduce it

Issues that are labeled as pending will not be automatically marked as stale.

I can confirm this does still happen with Nuxt 2.10.2 when page transitions are enabled.

I used to have these objects in my Nuxt config file in order to enable transitions:

layoutTransition: {
    name: 'layout',
    mode: ''
},

pageTransition: {
    name: 'page',
    mode: ''
}

I had to remove both of them to prevent pages components to be mounted/created twice.

As a temporary fix, I wrapped my code inside mounted in if condition. The condition is just to check if the layout name is the one I am expecting.

if (this.$nuxt.layoutName === 'myLayout') {
  ...
}

Don't know if this can help, but I managed to find my problem by putting breakpoints inside the fetch methode to realize the page was being loaded twice, by two different methods.

We had an el-menu made by Element-UI, that had inside of it, nuxt-links, so the route was loaded twice simultaneously.

I realize this was a coding error but this might help anyone looking for awnsers. Try to trace how the route gets changed !

My temporary workaround.

~/plugins/inject-layout.js

export default (context, inject) => {
  inject('layout', {

    // Layout name and route.name of pages that are not dafault layout
    map: {
      'one-column': ['signup', 'signin'],
      'some-other-layout': ['some-route-name']
    },

    // Property that represents correct layout name of current page
    current: 'default'
  })
}

nuxt.config.js

export default {
  plugins: ['~/plugins/inject-layout.js']
}

~/mixins/layout.js

export default {

  // Set and keep correct layout name of current page
  layout({ app, route }) {
    let layoutName = 'default'
    for (const [key, value] of Object.entries(app.$layout.map)) {
      if (value.includes(route.name)) layoutName = key
    }
    app.$layout.current = layoutName
    return layoutName
  },

  data() {
    return {
      // True while duplicated lifecycle hooks are running
      incorrectLayout: this.$nuxt.layoutName !== this.$layout.current
    }
  }
}

~/pages/*/.vue

import layout from '~/mixins/layout'

export default {

  // Layout setting
  mixins: [layout],

  // Hooks will be executed twice, but
  mounted() {
    // Return immediately if duplicated runs
    if (this.incorrectLayout) return
    console.log('This is only displayed once.')
  }
}

This is still occurring for me as of today, in [email protected]. My Vue browser extension reports that in the default layout, there are two views and thus everything occurs twice. This is in dev mode.

While running in dev mode, if I remove the element from layouts/default.vue, the page structure is cleared. Adding it back, one view appears (according to the Vue browser extension). After that, refreshing the page results in two views, indefinitely.

Given that this is happening within Nuxt, I don't know of any workarounds or logical statements that can prevent it.

image

I have the same issue with twice mounted on nuxt, what I did is this.

<client-only> <nuxt /> // On your layout <client-only>

hope it helps.

I had something similar
the problem was in the incorrectly registered plugin

@tsarrB can you provide any details? examples?

@gerasimvol
my case is described here

That's still a huge problem, some components mounts twice - that's lead to visual bugs (for example, i have component that mount maps-script, and that leads for two maps visible).

I disabled SSR, cause site now in just demo mode, but I'll need SSR soon. Using generate mode.

Any updates?

I'm facing the same issue! Using "nuxt": "^2.11.0". Need to check layout name before firing stuff inside created hook to prevent firing twice :(

Same issue. beforeDestroy fires twice too,and I cannot get the right this.$nuxt.layoutName in beforeDestroy

After a few weeks studying this issue, I've found another "fix" using Vuex / Nuxt Store.

By putting a 'v-if' statement on the <nuxt/> element on the layout itself you can prevent it from loading the content of the linked route before the layout changes.

This way you won't need to deal with every 'created' or 'mounted' function on your page components.

What I did:

Step 1) create a pages.js store module to register the content status.
IMPORTANT: to use vuex modules like this, you need to create an index.js file on ~/store folder

~/store/pages.js

export const state = () => ({
  showLayoutContent: true // show by default in case of first access or refreshing the page
})

export const mutations = {
  TOGGLE_LAYOUT_CONTENT (state, isVisible) {
     state.showLayoutContent = isVisible
  },
}

Step 2) Create an app.js middleware to hide the content on every route change

~/middleware/app.js

export default function (context) {
  // hide the content
  context.store.commit('pages/TOGGLE_LAYOUT_CONTENT', false)
}

Step 3 Add the middleware reference to your nuxt.config.js to extend the router property

~/nuxt.config.js

//{...}
router: {
    middleware: ['app']
},
//{...}

Step 4 Enable and use the store on your layout.

Ex: ~/layouts/dashboard.vue

<template>
 {...}  
    <!-- Page Content -->
    <nuxt v-if="showContent" />
 {...}
</template>

<script>
export default {
  computed: {
    showContent () {
      return this.$store.state.pages.showLayoutContent
    },
  },

  beforeCreate () {
    this.$store.commit('pages/TOGGLE_LAYOUT_CONTENT', true)
  }

}
</script>

Extra) In my case I've used the vue extends property for my Layout components. The ~/layouts/default.vue layout being the base and all other layouts extending from it.

This way the computed() and beforeCreated() are created only once and I can reuse the showContentcomputed property on the extended Layout files.

Hope it helps

same here https://github.com/nuxt/nuxt.js/issues/6961
cause serious visual issues 🙁

Will look at it, sorry for the delay 😬

Update: the issue seems to come only when doing server-side rendering (no issue with nuxt dev --spa).

I looked at the Vue Devtools and indeed, the default layout looked different when server-rendered <Default> vs client-side <layouts/default.vue> (when navigating on error page for example)

SSR:
Screenshot 2020-03-25 at 17 57 37

After navigating:
Screenshot 2020-03-25 at 17 57 41

So it actually re-render the whole layout, causing the new mounted() hooks.

Keeping the investigation 🕵🏻‍♂️

I found the issue, apparently the name of the layout is not defined, which cause a different name between server-side rendering and client-side (Default on SSR and layouts/default.vue on client).

As a quick workaround: Set a name for each of your layouts:

layouts/default.vue

<template>...</template>

<script>
export default {
  name: 'default'
}
</script>

I will make a PR to fix it in the next patch release of NuxtJS.

Thank you very much for reporting this and sorry for this outstanding delay 😞

Fixed in V2.12.1 release 🔥

For me, it is still calls mounted twice.

@asolopovas Would you please fill in a new issue with reproduction code?

Still happens in v2.12.2

Still happens in v2.12.2

Then create a new issue and a reproduction CodeSandbox or repo 😉

@manniL why not reopen this issue? i have same issue

<template>
  <div id="layout-web">
    <span> <!--  there has a span,it works -->
      <nuxt />
    </span>
    <SignDialog />
  </div>
</template>
<template>
  <div id="layout-web">
    <nuxt /> <!--  has same level html, has bug, mounted twice  -->
    <SignDialog />
  </div>
</template>

Same issue on 2.12.2

Yep, still happening on 2.12.2

still happening

image

Then create a new issue and a reproduction CodeSandbox or repo 😉

@Atinux
First of all, thanks for you effort and suggestion!

Using nuxt 2.12.2 and still have the problem
I am using both SPA and SSR

Screen Shot 2020-05-21 at 11 51 32

Screen Shot 2020-05-21 at 11 54 22

Screen Shot 2020-05-21 at 11 52 35

I found the issue, apparently the name of the layout is not defined, which cause a different name between server-side rendering and client-side (Default on SSR and layouts/default.vue on client).

As a quick workaround: Set a name for each of your layouts:

layouts/default.vue

<template>...</template>

<script>
export default {
  name: 'default'
}
</script>

I will make a PR to fix it in the next patch release of NuxtJS.

Thank you very much for reporting this and sorry for this outstanding delay 😞

tried this but have not resolved, any other suggestion :(

Hi - I am getting this error as well. I followed this pattern to create nested layout: https://constantsolutions.dk/2020/02/nested-layouts-in-nuxt-vue-js/ I am not sure if the other guys uses "slots" as well in their layout, and that's why it is happening?

As an example, in my pages/_slug.vue I have this:

mounted () {
    console.log('Mounted!', this.$nuxt.layoutName, this.$nuxt);
}

And in the console I get:

image

The strange thing is that no matter what I set in the layout name, it will always return "page" or "default". Changing page layout name to "name: 'APage'," it will still say it mounted "page".

I believe this could be the reason why my Nuxt application is slow when changing pages, despite having an API response time of 50ms.

reopen this issue?

Hi @manniL, I wanted to make a reproducible case, so I forked @amritk's codesandbox, and updated to Nuxt version 2.12.2 - I can confirm I didn't have to make any changes for the error to appear.

It writes "I have been mounted" twice in the console, see here: https://codesandbox.io/s/nuxt-vuetify-0xz91?file=/package.json. I even tried to give a name to the layout file.

When switching to the about page:

i have been created. Layout: default 
i have been mounted. Layout: default 
i have been created. Layout: other 
i have been mounted. Layout: other 

It's also weird it calls the layout "other" when I call this.$nuxt.layoutName, because I explicitly set the name to be otherlayout.

I think it would be appropriate to reopen this issue. Also I believe it causes page switches to take extra long time - we just launched our production site: https://thejewelleryroom.com and when switching pages to a page that has been cached by Cloudflare, and the response time is 50-100ms, it still takes 1-2 seconds for the page to switch. Perhaps it is because it mounts the page twice?

This is a very serious bug. I cannot not generate unique id for a client side only map container because it is calculated twice. I can not push a value to my empty-by-default array because it is pushed twice.

@melihplt you can make this workaround in your mounted/created hook.

if (this.$nuxt.layoutName === 'myLayout') {
    // will only fire once
}

However, it does not solve the performance issues it generates.

My temporary workaround.

~/plugins/inject-layout.js

export default (context, inject) => {
  inject('layout', {

    // Layout name and route.name of pages that are not dafault layout
    map: {
      'one-column': ['signup', 'signin'],
      'some-other-layout': ['some-route-name']
    },

    // Property that represents correct layout name of current page
    current: 'default'
  })
}

nuxt.config.js

export default {
  plugins: ['~/plugins/inject-layout.js']
}

~/mixins/layout.js

export default {

  // Set and keep correct layout name of current page
  layout({ app, route }) {
    let layoutName = 'default'
    for (const [key, value] of Object.entries(app.$layout.map)) {
      if (value.includes(route.name)) layoutName = key
    }
    app.$layout.current = layoutName
    return layoutName
  },

  data() {
    return {
      // True while duplicated lifecycle hooks are running
      incorrectLayout: this.$nuxt.layoutName !== this.$layout.current
    }
  }
}

~/pages/*/.vue

import layout from '~/mixins/layout'

export default {

  // Layout setting
  mixins: [layout],

  // Hooks will be executed twice, but
  mounted() {
    // Return immediately if duplicated runs
    if (this.incorrectLayout) return
    console.log('This is only displayed once.')
  }
}

Why so complicated... And is it sure, that you (even if really loaded twice) don't need some of the mounded code?

From my point of view this issue seems only to happen, if layout isn't default layout.
I have this issue on different pages, that use layout-property with another layout than default. If I remove layout-property from page (or set it to default), it isn't mounted twice. If you switch back to another layout, it's again mounted twice.

But in both mounts, it is mounted with the same layout-name - so you cannot say "if layout default don't mount..."

I'd just insert this into the pages mounted-function - but only on pages that don't use default layout:

mounted() {
    ...
    if (window.mounted_componentname == true) return;
    window.mounted_componentname = true;
    ....
}

thats ugly, but it works for me...

have same issue

same issue...

same issue

same issue

This is still happening in 2.14.7
Mounted Twice

Was this page helpful?
0 / 5 - 0 ratings