使用@svgr/webpack导致Webpack打包体积过大的问题排查
问题解答
为什么@svgr/webpack会把SVG打包进bundle.js?
@svgr/webpack的核心功能是把SVG文件转换成React组件代码(比如将SVG标签转成JSX),webpack会把这段转换后的JS/JSX代码直接打包进bundle.js——这是它的默认行为,因为它本质是把SVG当作代码模块处理,而非静态资源。
你之前的配置里虽然加了url-loader,但loader执行顺序是从右到左(实际生效是从后往前),@svgr/webpack先把SVG转成了组件代码,url-loader拿到的已经不是原始SVG文件内容,根本起不到将资源转成DataURL或输出文件的作用,所以所有SVG都会以组件代码的形式嵌入bundle,导致体积暴涨。
能否保留@svgr优势的同时,将SVG作为外部资源加载?
可以,核心思路是区分SVG的使用场景:需要当作React组件使用(比如要修改颜色、尺寸、内部元素)的用@svgr处理,仅作为静态图片展示的当作外部资源输出。具体有两种实现方式:
方式1:用资源查询(Resource Query)区分用法
给webpack配置两条规则,通过导入时的query参数区分是作为组件还是静态资源:
module.exports = { // ...其他配置 module: { rules: [ // 处理带?component的SVG,转成React组件 { test: /\.svg$/i, issuer: /\.[jt]sx?$/, resourceQuery: /component/, use: ["@svgr/webpack"], }, // 处理不带query的SVG,当作静态资源输出 { test: /\.svg$/i, issuer: /\.[jt]sx?$/, resourceQuery: { not: [/component/] }, type: "asset", }, ], }, };
使用示例:
- 保留@svgr优势(当作组件,可通过props修改属性):
import LoadingSpinner from "./loading-spinner.svg?component"; const Thing = () => { // 直接通过props修改SVG的填充色、尺寸 return <LoadingSpinner fill="#ff0000" width="60px" />; };
- 作为外部资源加载(仅展示图片):
import LoadingSpinner from "./loading-spinner.svg"; const Thing = () => { return <img src={LoadingSpinner} alt="加载中" />; };
方式2:根据SVG大小自动区分
对小体积SVG用@svgr转成组件内联(减少HTTP请求),大体积SVG输出为外部文件(控制bundle体积),用webpack的oneOf实现规则优先级:
module.exports = { // ...其他配置 module: { rules: [ { test: /\.svg$/i, issuer: /\.[jt]sx?$/, oneOf: [ // 强制指定为资源的SVG(加?url) { resourceQuery: /url/, type: "asset/resource", }, // 小于20KB的SVG转成React组件内联 { parser: { dataUrlCondition: { maxSize: 20 * 1024, // 可根据需求调整阈值 }, }, use: ["@svgr/webpack"], }, // 大于20KB的SVG输出为外部文件 { type: "asset/resource", }, ], }, ], }, };
这种方式无需手动加query,webpack会自动根据SVG大小处理,既保留了小SVG内联组件的便捷性,又避免大SVG膨胀bundle体积。
内容的提问来源于stack exchange,提问作者Seth Lutske
相关产品推荐
相关产品推荐

