引入 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) => (

{"Rectangle 5"}

)

export default SvgComponent

还支持另一个值 ‘named‘,这个参数值则能获得期望的行为,导出的 svg 文件如下

import * as React from "react"

const SvgComponent = (props) => (

{"Rectangle 5"}

)

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 文件的引入。

如果使用中有任何问题,欢迎留言~ (^ _ ^)

推荐文章

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。