vitejs / vite

Next generation frontend tooling. It's fast!
http://vite.dev
MIT License
68.12k stars 6.14k forks source link

Vite generates multiple copies of the same stylesheet #4448

Closed ghost closed 1 year ago

ghost commented 3 years ago

Describe the bug

I am using the following vite config option to make global styles available in React components without requirement of usage of "@import '../../syles/main" in each component file:

css: {
    preprocessorOptions: {
      scss: {
        additionalData: `
          @import "./src/styles/main";
        `
      }
    }
  }

it comes from https://github.com/vitejs/vite/issues/832

My main.scss looks like:

@import "./node_modules/normalize.css/normalize";

@import 'fonts';
@import 'colors';
@import 'breakpoints';
@import 'animations';
@import 'layers';

where fonts, colors, breakpoints, animations, layers include global variables.

It works fine in part I can use my variables inside component without importing the main SCSS file, but such approach causes an issue that I have multiple copies of the same stylesheet in a head section of page ( see the screenshot: https://imgur.com/a/k6hGnbP )

Reproduction

  1. Create empty app (Vite, React, TSX)
  2. Create a global main.scss file
  3. Import partials _colors.scss and _fonts.scss to main.scss using @import
  4. Create vite.config.js with option similar to described in a bug description section
  5. Create at least two different components and add it to your App
  6. Add some variables to colors.scss and fonts.scss
  7. Use variables from colors.scss and fonts.scss in your two components from step 5 using build-in CSS modules support ([component].module.scss)
  8. Run your app and look at head section

Expected result: There is only one global stylesheet. Actual result: There are multiple copies of global stylesheet, where number of copies equals number of unique components,

UPDATE: I've created a simplified app which one reproduces the issue: https://github.com/tastytea-github/vite-global-import-issue-reproduction

  1. Clone repo
  2. Install deps with npm i
  3. Run app with npm run dev
  4. Look at head section of the rendered page in devtools

Expected state: styles from main.scss are added globally only once. Actual state: styles from main.scss are included to each unique component.

System Info

No

Used Package Manager

npm

Logs

No response

Validations

akrabdev commented 3 years ago

This may help with this issue:

vite/src/client/client.ts

Is this intended?

const supportsConstructedSheet = (() => {
  try {
    // new CSSStyleSheet()
    // return true
  } catch (e) {}
  return false
})()

Seems that

 if (!style) {
      style = document.createElement('style')
      style.setAttribute('type', 'text/css')
      style.innerHTML = content
      document.head.appendChild(style) 

in updateStyle is always triggered for some reason.

ghost commented 3 years ago

@akrabdev

Is this intended?

Intended to what? Sorry, I didn't get what you mean. I assume the code is commented because it refers to https://wicg.github.io/construct-stylesheets/ which one is still in "A Collection of Interesting Ideas".

in updateStyle is always triggered for some reason.

Sounds truly, but I currently have no idea why.

ghost commented 3 years ago

Please look at simplified app I've created to see the issue reproduction (look at UPDATE in the issue description).

ghost commented 3 years ago

@sodatea Relating to added feat-css tag, I want to emphasize this issue is a bug, but not a feature, because currently Vite produces duplicated CSS in a bundle.

haoqunjiang commented 3 years ago

feat: css means the issue is related to the CSS bundling feature of Vite. It's not indicating that it is a feature request.

jerryYuX commented 3 years ago

I also encountered the same problem. When I used antd, the antd style generated two copies, one in the xxx.css file and the other in xxx. js file, which resulted in an increase in the total size of my bundles.

jerryYuX commented 3 years ago

I hope it can be solved fastly.

samarth-math commented 3 years ago

Hey guys, running into the same issue, which causes somewhat reduced performance for us. Hope to see a solution or explanation soon!

ghost commented 3 years ago

Any updates on this issue?

ghost commented 3 years ago

@patak-js Hello sir. Could you be so kind to pay attention to this issue?

phasetri commented 2 years ago

This is a pretty huge issue. I tried building my project, and noticed that the compiled index.css file is enormous (over 1 MB) despite me only writing a few KB worth of CSS code. If you manually inspect the generated .css files, there are a lot of repeated CSS code.

EDIT: Currently using a workaround where I just import my global SCSS file in the top-most component of my project. In the case of Vue, it's the App.vue file:

<style lang="scss">   // Do not include the "scoped" attribute to allow the rules to propagate to the nested components.
@import "@/css/universal/universal.scss";
</style>
ghost commented 2 years ago

Any update on this issue?

samarth-math commented 2 years ago

bump

jrson83 commented 2 years ago

I'm having the issue with vite-plugin-ssr. I'm building a page with preact-client-routing example as startpoint. The issue exists inside this untouched repo. Just clone and try.

If running npm run dev the import './PageShell.css' inside PageShell.jsx is injected as stylesheet and additionally as <style /> tag in the head. You also see the styles always two times inside the styles inspector.

2022-02-14_15h38_14

If running npm run build and npm run server:prod it is injected twice as stylesheet, here _default.page.client.jsx.2942c7fb.css.

2022-02-14_15h39_07

I don't know if this is maybe related to vite-plugin-ssr.

ghost commented 2 years ago

Will check it in production build. It seems duplication of styles in dev mode is ok since Vite uses HMR. If production build doesn't generate duplicated styles, then is it enough.

gegorov commented 2 years ago

same problem even if you use individual imports of common scss file in scss componets files. each import ends up duplicated in final compiled styles.css. using @use instead of @import in .scss files doesn't help.

sapphi-red commented 2 years ago

This comment was wrong in some parts. Please read my next comment.

This is how sass works. It is not a good choice to import files which include styles with `additionalData` since it leads to duplicated styles. `additionalData` is prepended to **every** scss file which is imported from js. Some related urls: - https://stackoverflow.com/questions/24182734/how-to-avoid-duplication-of-styles-in-scss - https://github.com/webpack-contrib/sass-loader/issues/145 - https://github.com/vuejs/vue-loader/issues/110 - https://sass-lang.com/blog/the-module-system-is-launched
MrFoxPro commented 2 years ago

@sapphi-red additionalData does prepend, but @use should reuse style if it is already in head.

sapphi-red commented 2 years ago

Sorry my comment was wrong in some parts.

For the following reason, it is not a good choice to import files which include styles with additionalData since it leads to duplicated styles.


For example, if you set @use 'foo.scss' to additionalData and the follwing files exists.

// foo.scss
.foo { color: red; }
// bar.scss
.bar { color: green; }
// baz.scss
.baz { color: blue; }
// main.js
import './bar.scss'
import './baz.scss'

bar.scss becomes like below.

.foo { color: red; }
.bar { color: green; }

baz.scss becomes like below.

.foo { color: red; }
.baz { color: blue; }

So the final bundled css becomes like below.

.foo { color: red; }
.bar { color: green; }
.foo { color: red; }
.baz { color: blue; }

If all the scss is bundled at once, it could be deduped. But that is not possible to do during dev and also makes css splitting hard.

mikeplus64 commented 2 years ago

For the following reason, it is not a good choice to import files which include styles with additionalData since it leads to duplicated styles.

* Vite processes each css import (including sass/scss) indivisually. This is the same behavior with webpack.

* `additionalData` is prepended to **every** scss file which is imported from js.

I might be misinterpreting what you've said here, but for me with my development builds, I'm not using additionalData at all and am running into the issue of multiple copies of the same stylesheet being generated.

sapphi-red commented 2 years ago

It will happen by the same reason for an example like below.

/* foo.css */
.foo { color: red; }
/* bar.css */
@import './foo.css'
.bar { color: green; }
/* baz.css */
@import './foo.css'
.baz { color: blue; }
// main.js
import './bar.css'
import './baz.css'
wesleyboar commented 2 years ago

Will check it in production build. It seems duplication of styles in dev mode is ok since Vite uses HMR. If production build doesn't generate duplicated styles, then is it enough.

When I vite build instead of vite preview, the output CSS still has duplicate content.

@tastytea-github, did you experience the same?

Bunkerbewohner commented 2 years ago

I also have experienced this issue. In my case the same '.less' file is imported by several other modules, and this leads to several duplicate