CSS from multiple layouts is being included on individual pages

markplewis
0
markplewis
commented a year ago

Version

v1.4.2 - v2.4.2 (see below).

Reproduction link

https://github.com/markplewis/nuxt-layout-css-issue

Steps to reproduce

  1. Clone the following example repo.
  2. Run npm install then npm run dev.
  3. Navigate between the pages and inspect the CSS that's applied to the <body> element via your browser's dev tools.

https://github.com/markplewis/nuxt-layout-css-issue

What is expected ?

When a page specifies a layout, like this:

pages/index.vue:

<script>
  export default {
    layout: "home"
  }
</script>

Only the CSS from the corresponding layout file should be included in the page:

layouts/home.vue:

<style>
  body {
    background-color: steelblue;
  }
</style>

What is actually happening?

The CSS from every layout file is being included on every page, regardless of which layout is specified in the page's .vue file.

screenshot

Additional comments?

My example repo is using Nuxt Edge.

bug
0
manniL
5.7k
manniL
commented a year ago

Can confirm this bug (production).

"Workaround" which only hides the problem and works for just 1st render ☹️ :

 build: {
    splitChunks: {
      layouts: true
    }
}
0
markplewis
0
markplewis
commented a year ago

This is also a problem in Nuxt v1 (see the v1 branch of my demo repo). Styles from multiple layouts are being pulled into the page, but at least I'm not seeing styles from the default layout anymore:

screen shot 2018-09-12 at 7 55 33 am

On initial page load, the CSS is correct. It's only after I navigate to "page 2" then back again that the CSS from the other layout ends up on the page.

0
Legym
0
Legym
commented a year ago

It appears that vue-loader, the actually dependency vue-style-loader, is parsing the scss in each component (regardless of what component is being mounted) and is inlining them into the html document. I am currently running into this problem with themes in different layouts

https://github.com/nuxt/nuxt.js/issues/3852

My solution was to include a css file in the layout and have a hook to prevent themes from leaking into one another. I had to pull in a gulp file and have a separate build to compile my scss.

Layout A:

export default {
  name: 'LayoutA',
  head() {
    return {
      link: [
        { rel: 'stylesheet', href: '/css/LayoutA/app.min.css' }
      ],
      bodyAttrs: {
        class: 'LayoutA'
      }
    }
  }
}
</script>

Layout B:

export default {
  name: 'LayoutB',
  head() {
    return {
      link: [
        { rel: 'stylesheet', href: '/css/LayoutB/app.min.css' }
      ],
      bodyAttrs: {
        class: 'LayoutB'
      }
    }
  }
}
</script>
0
hareku
15
hareku
commented a year ago

I have same issue.

0
Legym
0
Legym
commented a year ago

@hareku I was able to solve my issue using

 build: {
    splitChunks: {
      layouts: true
    }
}
0
hareku
15
hareku
commented a year ago

@Legym
Thanks!!!

0
markplewis
0
markplewis
commented a year ago

I upgraded my demo project to Nuxt 2.1.0 and I'm still experiencing this problem. As @manniL stated above, adding splitChunks: { layouts: true } solves the problem for first render (i.e. loading the page directly or hard refreshing your browser), but the problem persists when navigating back and forth between pages after that.

0
Legym
0
Legym
commented a year ago

@markplewis I had a different folder setup than you. We created a theme system inside our nuxt build and each theme was locked into their own URL. We didn't go from one theme to another directly and so we didn't run into your issue.

screen shot 2018-10-02 at 7 50 31 pm

splitChunks: { layouts: true } did solve our issue because originally our styles in Layout would bleed into other themes. I.e Styles from Desktop were leaking into Mobile.

However I would say the issue you are having is definitely a bug. The only suggestion I have is to do something like this or include it's own css file until it's fixed.

Layout 1

<template>
  <div>
    <nuxt/>
  </div>
</template>

<script>
export default {
  head() {
    return {
      bodyAttrs: {
        class: 'layout1'
      }
    }
  }
}

</script>

<style>
  .layout1 {
    background-color: steelblue;
  }
</style>

Layout 2

<template>
  <div>
    <nuxt/>
  </div>
</template>

<script>
export default {
  head() {
    return {
      bodyAttrs: {
        class: 'layout2'
      }
    }
  }
}

</script>

<style>
  .layout2 {
    background-color: steelblue;
  }
</style>

Vue-style-loader will take any css in the <style> tags and inline them to the document. Apparently vue-style-loader doesn't remove inline styles when you switch back so the last inline css always takes effect. You'll need to bind to a node that changes when switching layouts.

0
sumeshkmp
0
sumeshkmp
commented 10 months ago

This issue is reproducible in Nuxt 2.2.0 when loading the layout dynamically.

build: {
splitChunks: {
layouts: true
}
}

Did not worked.

screen shot 2018-10-15 at 11 57 52 am

0
fbrbovic
17
fbrbovic
commented 10 months ago

Still experiencing this problem too, any solutions? , layouts true doesn't fix it.

0
Atinux
25.9k
Atinux
commented 9 months ago

We are currently working on a solution that could work without any changes, for both critical CSS and extractCSS modes. Stay tuned :)

0
fbrbovic
17
fbrbovic
commented 9 months ago

Thanks, my temporary solution was to import scss or styl file inside <script> tag which doesn't seem to leak css

0
hareku
15
hareku
commented 9 months ago

I want to use splitChunks with extractCSS option.

0
rikoz
0
rikoz
commented 9 months ago

Any solution yet, I'm experiencing same here. Please I'm seeing a lot of temporaty fix but no detailed explanation of them. I need your help

Thanks

0
jaredreich
3
jaredreich
commented 8 months ago

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

Oops Stale Bot - you had one job! 😂

Really excited for the Nuxt team to fix this one. Go team go! 😄
(please don't stale/expire this issue…)

0
menthol
5
menthol
commented 8 months ago

I find a way to make it work on production, (no way yet for development).

in nuxt.config.js

import crypto from 'crypto';

const BUILDID = crypto.randomBytes(10).toString('hex');

module.exports = {
   // ...
  build: {
    extractCSS: true,
    // ...
    filenames: {
      css: `${BUILDID}.[name].css`,
    },
    // ...
  },
  env: {
    BUILDID,
  },
};

in layouts/*.vue

<template>
  <nuxt />
</template>
<script>
  // Fake import, make extract css create a new file with specific chunkName
  export function css() { import(/* webpackChunkName: 'theme-default' */ '~/assets/style.css'); }

  export default {
    head() {
      return {
        link: [
          // integrate compiled file into current layout
          { type: 'text/css', rel: 'stylesheet', href: `/_nuxt/${process.env.BUILDID}.theme-default.css` },
        ],
      };
    },
  };
</script>

As the dev server use in-memory compilation, i wasn't able to find the correct path for integrate compiled css file into current page in development. If you want to try, make sure you freeze the extractCSS property, ex: ...Object.defineProperty({}, 'extractCSS', { value: true }),;

0
menthol
5
menthol
commented 8 months ago

So, after some investigation, I succeeded make it work in dev :

in nuxt.config.js

import crypto from 'crypto';

const BUILDID = crypto.randomBytes(10).toString('hex');

module.exports = {
  // ...
  modules: [
    // ...
    '~/modules/ForceExtractCSS',
  ],
  // ...
  build: {
    extractCSS: true,
    // ...
    filenames: {
      css: `${BUILDID}.[name].css`,
    },
    // ...
  },
  env: {
    BUILDID,
  },
};

in modules/ForceExtractCSS.js

export default function ForceExtractCSSModule() {
  this.nuxt.hook('ready', () => {
    this.options.build.extractCSS = true;
  });
}

and in layouts/*.vue

<template>
  <nuxt />
</template>
<script>
  // Fake import, make extract css create a new file with specific chunkName
  export function css() { import(/* webpackChunkName: 'theme-default' */ '~/assets/style.css'); }

  export default {
    head() {
      return {
        link: [
          // integrate compiled file into current layout
          { type: 'text/css', rel: 'stylesheet', href: `/_nuxt/${process.env.BUILDID}.theme-default.css` },
        ],
      };
    },
  };
</script>

Canvas : HMR isn't working, head() isn't recalculated after HRM call

0
fbrbovic
17
fbrbovic
commented 7 months ago

This is still broken in 2.4.0

0
b02505048
0
b02505048
commented 7 months ago

Yeah, still a problem in 2.4.0

0
markplewis
0
markplewis
commented 6 months ago

FYI: I just updated my demo project to Nuxt 2.4.3.

0
nick-lawrence
0
nick-lawrence
commented 6 months ago

@markplewis Are you still experiencing this issue in the latest version?

I am currently in the middle of build using Nuxt 2.4.0 and undesired multiple Layout/CSS cross-compilation is still an issue.

0
markplewis
0
markplewis
commented 5 months ago

@nick-lawrence I just upgraded my demo project to Nuxt 2.5 and I'm still experiencing this issue.

0
ryanwinchester
0
ryanwinchester
commented 5 months ago

So, I wanted my login page to have a coloured background and the default layouts to have a white background.

This silly workaround does the trick for me.

  // login.vue
  beforeCreate() {
    document
      .getElementsByTagName('html')[0]
      .setAttribute('style', 'background-color: #4fc08d')
  }
  // default.vue
  beforeCreate() {
    document.getElementsByTagName('html')[0].removeAttribute('style')
  }
0
TomFrajer
0
TomFrajer
commented 5 months ago

Default.vue layout should have <style scoped> in it AND all other custom layouts should NOT HAVE scoped styles… this fixed it for me.

0
er1x
0
er1x
commented 5 months ago

+1 (Nuxt v 2.4.0)

In my case not only the layout "leaks" CSS, "pages/index.vue" too.
I'm adding an extra layout and extra pages (using this layout) through a Nuxt module, as a some sort of sub-application:

this.addLayout(path.resolve(__dirname, 'custom/layout.vue'), 'custom')
this.extendRoutes((routes, resolve) => {
  routes.push({
    path: '/subapp',
    name: 'subapp',
    component: resolve(__dirname, 'custom/subapp.vue')
  })
})
0
padinko
5
padinko
commented 5 months ago

my workaround for now is

nuxt.config.js

build: {
    splitChunks: {
      layouts: true
    }
}

and then reload page beafore each layout change, mixin in all layouts:

    beforeCreate() {
        if (!process.server) {
            if (window.__NUXT_LAYOUT__ && window.__NUXT_LAYOUT__ !== this) {
                window.location.reload();
            }
            window.__NUXT_LAYOUT__ = this;
        }
    },
1
Informations
Bug ReportOpen
#c7723 - Created a year ago