引入 svg 文件
项目配置常见报错信息项目基础@svgr/webpack 配置安装依赖增加配置增加类型声明引入方式使用 ReactComponentexportType 参数
storybook 环境
附:Babel 配置 babel-plugin-inline-react-svg
项目配置
常见报错信息
由于项目事先未配置 svg 文件的编译方式,常见的报错信息有:
Unhandled Runtime Error Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it’s defined in, or you might have mixed up default and named imports.
项目基础
先来声明项目架构,基于 next.js 和 react 搭建的项目,均采用最新的版本。配置 svg 文件时,还需要保证 storybook 环境能够正常运行。
{
"next": "14.1.0",
"react": "^18.2.0",
"@storybook/nextjs": "^7.6.17",
"@svgr/webpack": "^8.1.0",
}
之前项目是使用的 Babel 来配置 svg 的引入,升级后希望体验 nextjs 的 swc编译速度。于是移除了项目中的 babel 包,全面拥抱 nextjs。(babel 配置会附在文章末端)
@svgr/webpack 配置
安装依赖
首先去安装这个依赖项目,svgr 官网
npm install --save-dev @svgr/webpack
# or use yarn
yarn add --dev @svgr/webpack
增加配置
在根目录的 next.config.js 中,增加 webpack 的配置
const nextConfig = {
... some_config,
webpack: (config, { webpack }) => {
// Grab the existing rule that handles SVG imports
const fileLoaderRule = config.module.rules.find((rule) =>
rule.test?.test?.('.svg'),
)
config.module.rules.push(
// Reapply the existing rule, but only for svg imports ending in ?url
{
...fileLoaderRule,
test: /\.svg$/i,
resourceQuery: /url/, // *.svg?url
},
// Convert all other *.svg imports to React components
{
test: /\.svg$/i,
issuer: fileLoaderRule.issuer,
resourceQuery: { not: [...fileLoaderRule.resourceQuery.not, /url/] }, // exclude if *.svg?url
use: ['@svgr/webpack'],
},
)
// Modify the file loader rule to ignore *.svg, since we have it handled now.
fileLoaderRule.exclude = /\.svg$/i
return config
}
}
由于 nextjs 对于 svg 的引入方式有自己的一套规则,在配置文件中需要找到这些规则并覆盖。
增加类型声明
如果项目使用了 typescript,则需要增加全局的类型声明
declare module '*.svg' {
import { FC, SVGProps } from 'react'
const content: FC
export default content
}
declare module '*.svg?url' {
const content: any
export default content
}
引入方式
基于上述的步骤,可以实现以下的引入方式
import Star from './star.svg'
const Example = () => (
)
使用 ReactComponent
Note: 由于之前是 cra 构建的项目,希望保留 svg 引入的方式,如下所示:
import {ReactComponent as Star} from './star.svg'
const Example = () => (
)
那么需要修改配置,及类型声明
// next.config.js
{
test: /\.svg$/i,
issuer: fileLoaderRule.issuer,
resourceQuery: { not: [...fileLoaderRule.resourceQuery.not, /url/] }, // exclude if *.svg?url
use: [
{
loader: '@svgr/webpack',
options: {
prettier: false,
svgo: false,
svgoConfig: {
plugins: [{ removeViewBox: false }],
},
titleProp: true,
ref: true,
exportType:'named'
},
}
]
},
)
exportType 参数
这个字段在官方文档中藏的还蛮深的,在 options 里翻了一遍没有找到,去使用实例中才看出来端倪 默认值为 ‘default’,这样导出的 svg 文件如下
import * as React from "react"
const SvgComponent = (props) => (
)
export default SvgComponent
还支持另一个值 ‘named‘,这个参数值则能获得期望的行为,导出的 svg 文件如下
import * as React from "react"
const SvgComponent = (props) => (
)
export { SvgComponent as ReactComponent }
更新类型声明
declare module '*.svg' {
import React = require('react');
export const ReactComponent: React.SFC
const src: string;
export default src;
}
storybook 环境
在 storybook 环境中同样也需要增加配置
/.storybook/main.js
const config = {
...some_config,
webpackFinal: (webpackConfig) => {
// This modifies the existing image rule to exclude .svg files
// since you want to handle those files with @svgr/webpack
const imageRule = webpackConfig.module.rules.find((rule) => rule?.['test']?.test('.svg'));
if (imageRule) {
// !! very important
imageRule['exclude'] = /\.svg$/;
}
// Configure .svg files to be loaded with @svgr/webpack
webpackConfig.module.rules.push({
test: /\.svg?$/,
oneOf: [
{
use: [
{
loader: '@svgr/webpack',
options: {
prettier: false,
svgo: false,
svgoConfig: {
plugins: [{removeViewBox: false}],
},
titleProp: true,
exportType:'named'
},
},
],
issuer: {
and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
},
},
],
})
return webpackConfig
}
}
export default config;
附:Babel 配置 babel-plugin-inline-react-svg
如果是使用 babel 来进行 svg 文件的配置,首先安装依赖包
npm install --save-dev babel-plugin-inline-react-svg
# or use yarn
yarn add --dev babel-plugin-inline-react-svg
项目根目录增加 .babelrc 文件,同样的在 .storybook 文件夹下增加该文件
// .babelrc
{
"presets": ["next/babel"],
"plugins": [
// https://github.com/airbnb/babel-plugin-inline-react-svg/issues/107#issuecomment-840413948
[
"inline-react-svg",
{
"svgo": {
"plugins": [
{
"name": "removeViewBox",
"active": false
}
]
}
}
]
]
}
这两种方法都可以很好的支持 svg 文件的引入。
如果使用中有任何问题,欢迎留言~ (^ _ ^)
推荐文章
发表评论