使用Encore、SVGR与React时SVG无法正确导入的问题
问题:Symfony + Encore + React 中SVG内联渲染失败
我维护的Symfony项目前端基于Encore构建,正在将部分前端重构为React。需要添加SVG图标,但无法用img标签引入,尝试用SVGR把SVG转成React组件,导入时却返回文件路径而非组件,导致createElement报错。
现有Encore配置
Encore .setOutputPath('web/build/react') // the public path used by the web server to access the previous directory .setPublicPath('/build/react') .addEntry('react', '/react/app.js') // enable source maps during development .enableSourceMaps(!Encore.isProduction()) // empty the outputPath dir before each build .cleanupOutputBeforeBuild() // show OS notifications when builds finish/fail .enableBuildNotifications() .enableSingleRuntimeChunk() .enableStimulusBridge('./react/controllers.json') .enableTypeScriptLoader() // create hashed filenames (e.g. app.abc123.css) .enableVersioning() .configureBabel((config)=>{ config.presets.push('@babel/preset-react') }) .addRule({ test: /\.(sass|scss|svg|png|jpe?g)$/, use: [ 'style-loader', { loader: 'css-loader', options: { modules: { localIdentName: "[local]-[hash:base64:8]" } } }, 'sass-loader' ] }) .addRule({ test: /\.svg/, type: "asset/resource", generator: { filename: "[path][name]-[hash][ext][query]" } }) var configReact = Encore.getWebpackConfig(); configReact.target = ['browserslist']; configReact.name = 'react'; configReact.resolve.alias = { "@symfony/stimulus-bridge/controllers.json": "/react/controllers.json", "@src": "/react/src", }
现有Icon组件代码
import React from "react"; import { DecoratedProps } from "@src/types/componentHelpers"; import arrow from "@src/assets/icons/arrow.svg"; export interface Props { icon: IconType } export default class Icon extends React.Component<DecoratedProps<Props>> { constructor(props: DecoratedProps<Props>) { super(props); } render() { return (<Arrow />) } }
问题原因
- Encore的webpack规则中,SVG被同时匹配了两条规则:一条是和样式、图片一起走
style/css/sass-loader,另一条是单独的asset/resource规则,导致webpack将SVG当作资源文件处理,导入时返回文件路径而非React组件。 - 组件中导入
arrow.svg后直接使用<Arrow />,但既未定义该组件,也未将导入的SVG转换为React组件。
解决方案
步骤1:修改Encore配置,添加SVGR支持
首先安装SVGR依赖:
npm install @svgr/webpack --save-dev # 或使用yarn yarn add @svgr/webpack --dev
然后调整Encore的webpack规则:
Encore // ... 保留其他原有配置 .addRule({ // 移除原有规则中的svg匹配,SVG不需要走css-loader流程 test: /\.(sass|scss|png|jpe?g)$/, use: [ 'style-loader', { loader: 'css-loader', options: { modules: { localIdentName: "[local]-[hash:base64:8]" } } }, 'sass-loader' ] }) // 针对JS/TSX文件中的SVG导入,用SVGR转为React组件 .addRule({ test: /\.svg$/i, issuer: /\.[jt]sx?$/, use: ['@svgr/webpack'], }) // 可选:如果需要在样式文件中导入SVG作为资源文件,保留这条规则 .addRule({ test: /\.svg$/i, issuer: /\.(css|scss|sass)$/, type: 'asset/resource', generator: { filename: "[path][name]-[hash][ext][query]" } }) // ... 保留其他原有配置
步骤2:调整Icon组件代码
现在导入SVG会直接得到React组件,修改组件代码如下:
import React from "react"; import { DecoratedProps } from "@src/types/componentHelpers"; // 注意组件名大写,符合React组件命名规范 import Arrow from "@src/assets/icons/arrow.svg"; // 定义允许的图标类型 export type IconType = 'arrow'; export interface Props { icon: IconType } export default class Icon extends React.Component<DecoratedProps<Props>> { render() { // 维护图标映射表,支持动态渲染 const IconComponent = { arrow: Arrow // 可添加更多图标组件 }[this.props.icon]; return IconComponent ? <IconComponent /> : null; } }
更推荐的函数式组件写法
import React from "react"; import { DecoratedProps } from "@src/types/componentHelpers"; import Arrow from "@src/assets/icons/arrow.svg"; export type IconType = 'arrow'; interface Props { icon: IconType; } const Icon: React.FC<DecoratedProps<Props>> = ({ icon }) => { const iconMap = { arrow: Arrow, // 可扩展更多图标 }; const IconComponent = iconMap[icon]; return IconComponent ? <IconComponent /> : null; }; export default Icon;
内容的提问来源于stack exchange,提问作者JorisWillig
相关产品推荐
相关产品推荐

