问题记录:使用 @svgr/webpack 配置 svg 文件引入

3 篇文章 0 订阅
2 篇文章 0 订阅
本文详细介绍了如何在基于next.js和React的项目中配置SVG文件,包括安装@svgr/webpack、处理常见错误、Babel与SWC编译的迁移、类型声明以及在Storybook环境中的配置,同时探讨了exportType参数的选择对SVG组件导出的影响。
摘要由CSDN通过智能技术生成

项目配置

常见报错信息

由于项目事先未配置 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<SVGProps<SVGElement>>
  export default content
}

declare module '*.svg?url' {
  const content: any
  export default content
}

引入方式

基于上述的步骤,可以实现以下的引入方式

import Star from './star.svg'

const Example = () => (
  <div>
    <Star />
  </div>
)

使用 ReactComponent

Note: 由于之前是 cra 构建的项目,希望保留 svg 引入的方式,如下所示:

import {ReactComponent as Star} from './star.svg'

const Example = () => (
  <div>
    <Star />
  </div>
)

那么需要修改配置,及类型声明

// 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) => (
  <svg xmlns="http://www.w3.org/2000/svg" width={48} height={1} {...props}>
    <title>{"Rectangle 5"}</title>
    <path fill="#063855" fillRule="evenodd" d="M0 0h48v1H0z" />
  </svg>
)
export default SvgComponent

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

import * as React from "react"
const SvgComponent = (props) => (
  <svg xmlns="http://www.w3.org/2000/svg" width={48} height={1} {...props}>
    <title>{"Rectangle 5"}</title>
    <path fill="#063855" fillRule="evenodd" d="M0 0h48v1H0z" />
  </svg>
)
export { SvgComponent as ReactComponent }

更新类型声明

declare module '*.svg' {
  import React = require('react');
  export const ReactComponent: React.SFC<React.SVGProps<SVGSVGElement>>;
  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 文件的引入。

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

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值