Skip to content
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

Duplicated CSS rules in production #46

Open
mrksbnch opened this issue Jan 6, 2019 · 15 comments
Open

Duplicated CSS rules in production #46

mrksbnch opened this issue Jan 6, 2019 · 15 comments

Comments

@mrksbnch
Copy link

mrksbnch commented Jan 6, 2019

When using this CLI plugin, there are duplicated CSS rules when using CSS modules and the "composes" feature. This seems to be caused by the critical CSS that's inlined into the <head> and is both happening in development and production (main issue).

I've created the following test repo (https://github.com/mrksbnch/vue-ssr-css-issue) that I created using vue create (Vue router as the only feature) and vue add @akryum/vue-cli-plugin-ssr. Afterwards, I added a test.css file in the folder assets and composed (<style module>) two style declarations from that file in Home.vue and HelloWorld.vue.

You can see the duplicated rules after running npm run ssr:serve or npm run ssr:build and npm run ssr:start in the dev tools:

screen shot 2019-01-06 at 18 32 42

Both CSS rules are inlined on the server side (<style data-vue-ssr-id>):

screen shot 2019-01-06 at 18 33 20

This is just a simple test repo with only one CSS rule. In a proper app this will cause a lot (!) of duplicated CSS rules and increase the file size by quite a bit. I've tested it with a simple app and I noticed some rules being duplicated 5 or 6 times.

@zickat
Copy link
Contributor

zickat commented Feb 7, 2019

I have the same issue, do you find a solution ?

@p1n5u
Copy link
Contributor

p1n5u commented Feb 8, 2019

Actually, the styles don't get added twice by the SSR plugin, but you get the styles delivered with the server side-rendered page and as soon as you mount the app on the client side on top of the pre-rendered dom, Vue adds the styles of the components again.
So it is not really a Bug or an Issue, but it is simply a reality of prerendering. Right now I am working on a solution to this (for example extract the CSS from the JS bundle and inject them into the HTML).
I keep you updated and tell you as soon as I've managed to solve this problem

@p1n5u
Copy link
Contributor

p1n5u commented Feb 8, 2019

The solution is really simple! The devs of vue-style-loader thought about this issue and already implemented a solution. To fix the duplicated style issue we simply need to configure the css loader to use the ssrId option:

ssrId (3.1.0+):
Type: boolean. Add data-vue-ssr-id attribute to injected <style> tags even when not in Node.js. This can be used with pre-rendering (instead of SSR) to avoid duplicate style injection on hydration.

To do this we simply need to add the loader config to our vue.config.js:

module.exports = {
  //...
  css: {
    loaderOptions: {
      css: {
        ssrId: true
      }
    }
  }
  //...
}

Hope it helps 😃

@Alik2015
Copy link

Alik2015 commented May 8, 2019

Hi So just to confirm when using SSR the csss file is created as expected and the same content is also added to <style ssr-id>?

The ssrId:true does not stop this from happening.

@le0l1
Copy link

le0l1 commented May 8, 2019

+1

@phiphan148
Copy link

The solution is really simple! The devs of vue-style-loader thought about this issue and already implemented a solution. To fix the duplicated style issue we simply need to configure the css loader to use the ssrId option:

ssrId (3.1.0+):
Type: boolean. Add data-vue-ssr-id attribute to injected <style> tags even when not in Node.js. This can be used with pre-rendering (instead of SSR) to avoid duplicate style injection on hydration.

To do this we simply need to add the loader config to our vue.config.js:

module.exports = {
  //...
  css: {
    loaderOptions: {
      css: {
        ssrId: true
      }
    }
  }
  //...
}

Hope it helps 😃

I tried but does not work. It still renders duplicated CSS

@PidginEnemy
Copy link

PidginEnemy commented Dec 6, 2019

I have the same problem. vue-cli 3 + vue-cli-ssr-plugin (default config) + vue-apollo
In my production code i see folowing: https://cl.ly/0b20bf11baa4
My css rules duplicates multiple times (2 or 3 times)
I can't understand what's wrong and can't find docs...
I tried paste ssrId option to vue.config.js and get the folowing error:
ValidationError: Invalid options object. CSS Loader has been initialised using an options object that does not match the API schema. - options has an unknown property 'ssrId'.
@p1n5u maybe you know what i do wrong ?

@Sporradik
Copy link

Sporradik commented Jan 28, 2020

@PidginEnemy @phiphan148

Those instructions that @p1n5u provided are perhaps a little bit misleading. vue-style-loader is not a supported loader for loaderOptions. See: https://cli.vuejs.org/config/#css-loaderoptions

There may be a way to get it to work like that, but I couldn't get it to work.

If you want to modify vue-style-loader options you can do so like this:

module.exports = {
	chainWebpack: config => {
		let types = ['vue-modules', 'vue', 'normal-modules', 'normal']
		types.forEach(type => {
			config.module.rule('css').oneOf(type).use('vue-style-loader').tap(options => {
				options.ssrId = true
				return options
			})
		})
	}
}

I was able to get the ssrIds working by doing this.

If you are using SCSS or similar things, you may have to do this multiple times replacing 'css' with the appropriate language. You can see someone modifying the vue-style-loader config like this here: vuejs/vue-style-loader#40

@enriqg9
Copy link

enriqg9 commented Apr 10, 2020

@PidginEnemy @phiphan148

Those instructions that @p1n5u provided are perhaps a little bit misleading. vue-style-loader is not a supported loader for loaderOptions. See: https://cli.vuejs.org/config/#css-loaderoptions

There may be a way to get it to work like that, but I couldn't get it to work.

If you want to modify vue-style-loader options you can do so like this:

module.exports = {
	chainWebpack: config => {
		let types = ['vue-modules', 'vue', 'normal-modules', 'normal']
		types.forEach(type => {
			config.module.rule('css').oneOf(type).use('vue-style-loader').tap(options => {
				options.ssrId = true
				return options
			})
		})
	}
}

I was able to get the ssrIds working by doing this.

If you are using SCSS or similar things, you may have to do this multiple times replacing 'css' with the appropriate language. You can see someone modifying the vue-style-loader config like this here: vuejs/vue-style-loader#40

@Sporradik I followed with SCSS this but I am getting an error.

TypeError: Cannot set property 'ssrId' of undefined

Then if I initialize the options as shown on vuejs/vue-style-loader#40 I get:

Error: No loader specified

And if I specify a loader my build fails.

.use('vue-style-loader')
.loader('vue-style-loader')
...
SassError: SassError: expected "{".
  ╷
5 │ var content = require("!!../node_modules/cache-loader/dist/cjs.js??ref--0-0!../node_modules/vue-loader/lib/index.js??vue-loader-options!./App.vue?vue&type=style&index=0&lang=scss&");

@Sylvadoc
Copy link

Same issue with @enriqg9 :( I also try to add the data-vue-ssr-id boolean on style tags but it doesn't fix the duplication :(

cf https://github.com/vuejs/vue-style-loader#options

@usedjimmy
Copy link

you guys may want to take a look at this #158

@enriqg9
Copy link

enriqg9 commented Apr 24, 2020

@usedjimmy thanks, that is a separate issue, already implemented that solution to avoid duplicate JS files.

This seems to be caused by the critical CSS that's inlined into the and is both happening in development and production (main issue).

@emanuelverardi
Copy link

Same issue with me. For some strange reason, when I mixed some 3'rd party lib's this stopped happening in one of my projects.
For all other projects using Vue Cli 3 and SSR I'm getting the css inline on the head of my html.
ex: <style data-vue-ssr-id="d2c949be:0 52a1d275:0"> css code here </style data-vue-ssr-id>.
The reason to use SSR is to have a good SEO and inlining the css jut make it terrible...
Anyone have any idea why this is happening ? Thanks.

@sneko
Copy link

sneko commented Jul 8, 2020

Just to follow up... same for me since a long time. I don't get why by default we cannot have CSS extracted into separate files, but also even if I succeed doing that with some patchy settings, why they are rendered twice as you described.

It makes a pain to manage and to serve a proper website to visitors 😢 ...

@Sporradik
Copy link

Sporradik commented Jul 9, 2020

@enriqg9 We don't use SCSS, so I'm not sure if I can be of much support, but I have some ideas of what you could try:

I would recommend taking a look at your built webpack config using vue inspect > output.js and then taking a look a the generated output.js file. It might give you some insight into the structure of your loaders and why it can't seem to find options for vue-style-loader under the scss rule.

Is it just throwing that error for scss or for css as well?

Is it possible that one of the "types" is not relevant in your case, so that is causing an error? If so, you could try removing it.

You could try only modifying options if they exist. If you can just prevent the error with an undefined check, then maybe it could work.

You can put console.logs in your chainWebpack function and they will log in the terminal where you run vue-cli-service start. It's a bit hard to read, but you could use this to figure out which type or rule is causing the error.

Webpack is very confusing to me, especially with vue-cli so I am sorry that I can't be of more help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests