Subscribe on changes!

Multi-page splits css into file that is never imported

avatar
Oct 6th 2023

Vue version

3.3.4

Link to minimal reproduction

https://git.sr.ht/~whynothugo/vue-multi-page-style-reproducer

Steps to reproduce

git clone https://git.sr.ht/~whynothugo/vue-multi-page-style-reproducer
cd vue-multi-page-style-reproducer
npm exec -- vite build

What is expected?

For each entry point, a javascript and a css file should be created. If any code is split into separate files, those must be loaded by the entry point file.

What is actually happening?

When multiple entry points share a component (but not ALL entry points), the styles for this component are extracted into a separate CSS file.

From the reproduction example:

> npm exec -- vite build                                      
vite v4.4.11 building for production...
✓ 19 modules transformed.
dist/assets/one-3efe7ca5.css                        0.03 kB │ gzip:  0.05 kB
dist/assets/two-1ab739ba.css                        0.03 kB │ gzip:  0.05 kB
dist/assets/WithStyle-01282682.css                  0.04 kB │ gzip:  0.06 kB
dist/assets/three-39ee0a8d.js                       0.19 kB │ gzip:  0.17 kB
dist/assets/WithStyle-474dfed4.js                   0.25 kB │ gzip:  0.22 kB
dist/assets/two-2e290a12.js                         0.38 kB │ gzip:  0.28 kB
dist/assets/one-6e5138cf.js                         0.38 kB │ gzip:  0.29 kB
dist/assets/_plugin-vue_export-helper-96a61a55.js  48.91 kB │ gzip: 19.76 kB
✓ built in 455ms

Note that nothing imports this separate file (in this case WithStyle-01282682.css), so it is not loaded in the final page.

System Info

> npx envinfo --system --npmPackages vue --binaries --browsers

  System:
    OS: Linux 6.5 Alpine Linux
    CPU: (24) x64 13th Gen Intel(R) Core(TM) i7-13700K
    Memory: 8.07 GB / 62.57 GB
    Container: Yes
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 18.18.0 - /usr/bin/node
    npm: 10.1.0 - /usr/bin/npm
  npmPackages:
    vue: ^3.3.4 => 3.3.4 

It's likely irrelevant, but the information above is wrong; this is not a container.

Any additional comments?

I previously asked about this in "Discussions": https://github.com/vuejs/core/discussions/9326

avatar
Oct 8th 2023

If any code is split into separate files, those must be loaded by the entry point file.

That's the case for HTML entries. Vite expects HTML files as entry points by default, and the script and style files would be included in the compiled HTML file. See the example configuration at https://vitejs.dev/guide/build.html#multi-page-app

On the other hand, according to the description in the corresponding discussion thread, you are trying to integrate Vite into your backend with manifest: true.

In that case, this isn't done for you automatically; you need to get all the imported chunks' CSS files by yourself. And all the necessary information is available in the manifest.json. It's just a bit tedious.

https://vitejs.dev/guide/backend-integration.html Note this line:

Chunks will contain information on its static and dynamic imports (both are keys that map to the corresponding chunk in the manifest), and also its corresponding CSS and asset files (if any).

In the example case, the chunk src/one.ts imports the chunk _WithStyle-474dfed4.js, the chunk information of _WithStyle-474dfed4.js contains a css key:

"_WithStyle-474dfed4.js": {
    "css": [
      "assets/WithStyle-01282682.css"
    ],
    "file": "assets/WithStyle-474dfed4.js",
    "imports": [
      "__plugin-vue_export-helper-96a61a55.js"
    ]
  }
avatar
Oct 8th 2023

Thanks for your reply, this is very helpful. I hadn't really picked up on the implications of that mention in the docs (I'm sure I've read that whole page a few times now trying to figure this one out).

Adding additional tags to load additional css files is a rather complicated in my current setup. Is it possible to disable this type of css chunking altogether? I'm honestly okay with duplicating them in this case (the styles are merely a few dozen bytes anyway).

avatar
Oct 8th 2023

There's no built-in solution. But this plugin with some customization seems to work:

https://www.npmjs.com/package/vite-plugin-css-injected-by-js

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";

// https://vitejs.dev/config/
export default defineConfig({
  build: {
    manifest: true,
    rollupOptions: {
      input: ["src/one.ts", "src/two.ts", "src/three.ts"],
    },
  },
  plugins: [
    vue(),
    cssInjectedByJsPlugin({
      jsAssetsFilterFunction: function customJsAssetsfilterFunction(
        outputChunk
      ) {
        return outputChunk.isEntry;
      },
    }),
  ],
});