Vite配置:根据目录名设置assetFileNames命名规则
解决Vite打包时页面目录下index.css重命名为目录名的问题
项目结构如下,每个页面目录下的style.css被index.jsx引入后,Vite打包会将所有这类CSS输出为index.<hash>.css,调试时难以区分:
src/ --pages/ ----Home/ ------index.jsx ------style.css ----NotFound/ ------index.jsx ------style.css
assetFileNames的assetInfo参数仅包含name、source和type,无法直接获取原始文件路径,不像chunkFileNames可以通过facadeModuleId拿到源目录来重命名chunk。
方法:通过Rollup的generateBundle钩子关联Chunk与Asset
利用Rollup的generateBundle钩子,遍历打包产物中的Chunk和Asset,通过Chunk的facadeModuleId找到对应页面的目录名,再匹配关联的CSS Asset进行重命名。
完整配置代码
import { defineConfig } from 'vite'; import path from 'path'; export default defineConfig({ build: { rollupOptions: { output: { assetFileNames: 'assets/[name]-[hash][extname]', chunkFileNames: (chunkInfo) => { const name = chunkInfo.name === 'index' ? path.basename(path.dirname(chunkInfo.facadeModuleId)) : chunkInfo.name; return `${name}-[hash].js`; }, }, plugins: [ { name: 'rename-index-css', generateBundle(_options, bundle) { // 遍历所有Chunk,每个Chunk对应一个页面的入口文件 Object.values(bundle).forEach(chunk => { if (chunk.type === 'chunk' && chunk.facadeModuleId) { const pageDir = path.basename(path.dirname(chunk.facadeModuleId)); // 遍历所有Asset,寻找当前Chunk关联的index.css Object.values(bundle).forEach(asset => { if (asset.type === 'asset' && asset.name === 'index.css') { // 验证该CSS文件是否属于当前页面目录 if (chunk.modules) { const matchedCss = Object.keys(chunk.modules).find(modulePath => modulePath.endsWith('style.css') && path.dirname(modulePath) === path.dirname(chunk.facadeModuleId) ); if (matchedCss) { // 将文件名中的index替换为页面目录名 asset.fileName = asset.fileName.replace('index-', `${pageDir}-`); } } } }); } }); } } ] } } });
工作原理
- Chunk遍历:每个页面的
index.jsx会被打包成一个Chunk,通过chunk.facadeModuleId可以获取该入口文件的绝对路径,进而提取出页面目录名(如Home、NotFound)。 - Asset匹配:遍历所有打包生成的Asset,找到名为
index.css的文件,通过Chunk的modules属性验证该CSS是否属于当前页面目录。 - 重命名Asset:确认匹配后,将CSS文件名中的
index-替换为页面目录名,最终输出如Home-<hash>.css、NotFound-<hash>.css的文件。
替代方案(手动指定名称,不推荐)
如果不想用钩子,也可以在引入CSS时通过查询参数指定名称:
// Home/index.jsx import './style.css?name=Home'; // NotFound/index.jsx import './style.css?name=NotFound';
然后在assetFileNames中解析查询参数:
assetFileNames: (assetInfo) => { let name = assetInfo.name; // 解析查询参数中的name if (assetInfo.source?.includes('?name=')) { name = assetInfo.source.split('?name=')[1].split('&')[0]; } return `assets/${name}-[hash][extname]`; }
这种方法需要手动修改每个页面的引入代码,灵活性较差,适合小型项目。
内容的提问来源于stack exchange,提问作者rschristian
相关产品推荐
相关产品推荐

