设计系统中动态加载SVG图标导致TypeScript报错及Webpack加载器问题的解决方案咨询
我最近在维护一个设计系统库,里面的Icon组件原本是通过静态导入所有SVG图标的方式实现的,结果导致打包后的体积直接冲到了6MB,这实在太夸张了!
先跟大家说下原本的实现:我有个images.ts用来统一导出所有SVG图标:
export { default as SvgArrow } from './arrow.svg'; export { default as SvgCopy } from './copy.svg'; ... export { default as SvgWater } from './water.svg';
然后Icon组件里直接导入所有图标,根据传入的iconName来渲染对应的图标:
import * as Icons from '../images'; // 渲染时获取当前图标 const CurrentIcon = Icons[iconName];
这种写法虽然简单,但所有用了<Icon />的组件都会把全部SVG打包进去,包体积直接炸了。
为了瘦身,我改成了动态加载当前需要的图标,代码是这样的:
const [IconComponent, setIconComponent] = useState<React.ElementType | null>( null, ); useEffect(() => { const loadIcon = async () => { const icons = await import('../icons'); setIconComponent(() => icons[iconName]); }; loadIcon(); }, [iconName]);
改完之后效果立竿见影,包体积直接从6MB降到了700KB!但新的问题又来了——TypeScript疯狂报错,在使用这个设计系统的消费应用里,满屏都是类似这样的错误:
You may need an appropriate loader to handle this file type, currently
no loaders are configured to process this file.
只要我把代码改回静态导入,所有错误就都消失了,但包体积又回到了6MB,这真的很矛盾。
我试过各种办法:Webpack魔法注释、用eval(虽然能跑,但这明显是歪门邪道,安全性和性能都没保障)、优化useEffect的类型定义,但都没用,还是一堆TS错误。
下面是我的tsconfig和Webpack配置,大家帮我看看问题出在哪?
我的tsconfig.json配置:
{ "compilerOptions": { "baseUrl": ".", "target": "es2020", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "strict": true, "declaration": true, "outDir": "./dist", "rootDir": ".", "declarationDir": "./dist", "forceConsistentCasingInFileNames": true, "esModuleInterop": true, "module": "es2020", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "sourceMap": true, "declarationMap": true, "jsx": "react-jsx", "incremental": false, "paths": { "components/*": ["components/*"], "utils/*": ["utils/*"], }, "plugins": [] }, "include": ["**/*.ts", "**/*.tsx"], "exclude": ["node_modules", "dist"] }
Webpack配置文件:
const path = require('path'); const TerserPlugin = require('terser-webpack-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const { getEntries } = require('./getEntries'); module.exports = { entry: getEntries(), mode: 'production', output: { path: path.resolve(__dirname, 'dist'), filename: (pathData) => { const name = pathData.chunk.name; return `${name}.js`; }, libraryTarget: 'module', globalObject: 'this', }, module: { rules: [ { test: /\.(ts|tsx)$/, exclude: /node_modules/, use: { loader: 'babel-loader', }, }, { test: /\.svg$/, use: [ { loader: '@svgr/webpack', options: { typescript: true, ref: true, }, }, ], }, ], }, resolve: { extensions: ['.ts', '.tsx', '.js', '.jsx'], alias: { components: path.resolve(__dirname, 'components'), theme: path.resolve(__dirname, 'theme'), images: path.resolve(__dirname, 'images'), types: path.resolve(__dirname, 'types'), }, }, externals: { '@mui/material': '@mui/material', next: 'next', react: 'react', 'react-dom': 'react-dom', }, optimization: { minimizer: [new TerserPlugin()], }, plugins: [new CleanWebpackPlugin()], experiments: { outputModule: true, }, };
我还查过相关的问题,试过各种公开方案,除了eval都不管用。另外说一下,我用了@svgr/webpack来处理SVG,静态导入的时候完全没问题,图标都能正常渲染。
现在我发现如果在消费应用里加个忽略.d.ts的loader,应用能跑,但这显然不是长久之计——我希望我的设计系统库能开箱即用,用户不用额外配置就能正常使用,同时还能享受到动态加载带来的体积优化。
有没有大佬能帮我看看这个问题到底该怎么解决?
备注:内容来源于stack exchange,提问作者marcus-linmarkson

