You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Vite Glob Import动态生成React SVG图标组件并正常构建?

解决方案

问题根源

你遇到的核心问题是:import.meta.glob({ eager: true }) 在Vite库构建阶段,Rollup无法完全静态解析动态导入的SVG资源,导致最终打包产物里没有包含这些图标组件;同时当前导出的是一个对象,组件库的消费方也无法通过树摇按需引入单个图标。

方案一:优化Vite配置与导出逻辑,让Rollup静态解析Glob导入

1. 调整Vite配置

修改vite-plugin-svgr的配置,确保SVG被正确编译为React组件,同时让Rollup能识别Glob导入的静态资源:

import react from '@vitejs/plugin-react';
import { resolve } from 'path';
import { defineConfig } from 'vite';
import dts from 'vite-plugin-dts';
import tsConfigPaths from 'vite-tsconfig-paths';
import * as packageJson from './package.json';
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';
import svgr from 'vite-plugin-svgr';

export default defineConfig(() => ({
  plugins: [
    cssInjectedByJsPlugin(),
    // 调整svgr配置,明确处理路径,开启named导出
    svgr({
      include: './src/assets/icons/*.svg',
      svgrOptions: {
        exportType: 'named',
      },
    }),
    react(),
    tsConfigPaths() as any,
    dts({
      include: ['src'],
      // 让dts插件识别动态生成的组件类型
      rollupTypes: true,
    }),
  ],
  build: {
    lib: {
      entry: resolve('src', 'index.ts'),
      name: 'red-mist',
      formats: ['es', 'cjs'],
      fileName: (format) => `my-lib.${format}.js`,
    },
    rollupOptions: {
      external: [...Object.keys(packageJson.peerDependencies)],
      input: {
        index: resolve('src', 'index.ts'),
        css: resolve('src', 'index.css'),
      },
      output: {
        preserveModules: true, // 保留模块结构,利于树摇
        preserveModulesRoot: 'src',
      },
    },
  },
}));

2. 修改图标组件导出代码

把动态导出的对象改成具名导出,让Rollup能静态识别每个组件:

// src/components/Icons/index.ts
import type { FC, SVGProps } from 'react';

const svgs = import.meta.glob('./../../assets/icons/*.svg', { 
  eager: true,
  import: 'ReactComponent', // 直接导入ReactComponent,简化逻辑
});

const exports: Record<string, FC<SVGProps<SVGSVGElement>>> = {};

for (const [path, component] of Object.entries(svgs)) {
  // 从路径提取文件名,生成规范组件名(比如"user.svg" -> "UserIcon")
  const fileName = path.split('/').pop()?.replace('.svg', '') || '';
  const componentName = fileName.charAt(0).toUpperCase() + fileName.slice(1) + 'Icon';
  exports[componentName] = component as FC<SVGProps<SVGSVGElement>>;
}

// 导出所有组件(支持按需引入)
export const { ...Icons } = exports;
// 兼容原有默认导出用法
export default exports;

3. 在入口文件index.ts中导出图标组件

// src/index.ts
export * from './components/Icons';

方案二:构建前预生成图标组件文件(最可靠)

如果方案一仍有问题,直接用Node脚本在构建前自动生成静态的图标组件文件,彻底避免动态导入的问题:

1. 编写预生成脚本(scripts/generate-icons.ts)

import fs from 'fs';
import path from 'path';

const iconsDir = path.resolve(__dirname, '../src/assets/icons');
const outputFile = path.resolve(__dirname, '../src/components/Icons/generated.tsx');

// 读取icons文件夹下的所有SVG文件
const svgFiles = fs.readdirSync(iconsDir).filter(file => file.endsWith('.svg'));

// 生成导入语句
const importStatements = svgFiles.map(file => {
  const fileName = file.replace('.svg', '');
  const componentAlias = fileName.charAt(0).toUpperCase() + fileName.slice(1);
  return `import { ReactComponent as ${componentAlias} } from './../../assets/icons/${file}';`;
}).join('\n');

// 生成具名导出语句
const exportStatements = svgFiles.map(file => {
  const fileName = file.replace('.svg', '');
  const componentAlias = fileName.charAt(0).toUpperCase() + fileName.slice(1);
  const componentName = `${componentAlias}Icon`;
  return `export const ${componentName} = ${componentAlias};`;
}).join('\n');

// 生成默认导出
const defaultExport = `export default { ${svgFiles.map(file => {
  const fileName = file.replace('.svg', '');
  const componentName = fileName.charAt(0).toUpperCase() + fileName.slice(1) + 'Icon';
  return componentName;
}).join(', ')} };`;

// 拼接完整代码并写入文件
const content = `
${importStatements}

${exportStatements}

${defaultExport}
`.trim();

fs.writeFileSync(outputFile, content);
console.log('图标组件文件已生成');

2. 在package.json中添加预构建脚本

{
  "scripts": {
    "prebuild": "tsx scripts/generate-icons.ts",
    "build": "vite build"
  }
}

3. 在Icons/index.ts中导入生成的组件

// src/components/Icons/index.ts
export * from './generated';
export { default } from './generated';

这个方案的优势是:所有图标组件都是静态导入,构建时Rollup能完全解析,类型生成也不会有问题,消费方可以直接按需引入单个图标。

验证方法

  1. 执行npm run build后,检查打包产物中的my-lib.es.js或my-lib.cjs.js,确认包含图标组件的代码
  2. 在测试项目中安装组件库,尝试导入单个图标组件(比如import { UserIcon } from 'red-mist'),验证是否能正常渲染

内容的提问来源于stack exchange,提问作者counterbeing

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 00:32:52