Multi-page splits css into file that is never imported
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
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"
]
}
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).
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;
},
}),
],
});