本文主要讨论基于webpack5+TypeScript的React项目(cra、craco底层本质都是使用webpack,所以同理)在2023年的今天是如何在项目中使用svg资源的。

首先,假定您已经完成基于webpack5+TypeScript的React项目的搭建工作(如果您不太清楚搭建的背景,可以参考这篇笔记:【个人笔记】2023年搭建基于webpack5与typescript的react项目 - 知乎 (zhihu.com))。

HTML中SVG经典用法

SVG:可缩放矢量图形 | MDN (mozilla.org)

要在一般的html中使用SVG,我们可以直接编写标签:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512" width="200" height="200">
<path d="M256 32C114.6 32 0 125.1 0 240c0 47.6 19.9 91.2 52.9 126.3C38 405.7 7 439.1 6.5 439.5c-6.6 7-8.4 17.2-4.6 26S14.4 480 24 480c61.5 0 110-25.7 139.1-46.3C192 442.8 223.2 448 256 448c141.4 0 256-93.1 256-208S397.4 32 256 32zm0 368c-26.7 0-53.1-4.1-78.4-12.1l-22.7-7.2-19.5 13.8c-14.3 10.1-33.9 21.4-57.5 29 7.3-12.1 14.4-25.7 19.9-40.2l10.6-28.1-20.6-21.8C69.7 314.1 48 282.2 48 240c0-88.2 93.3-160 208-160s208 71.8 208 160-93.3 160-208 160z"/>
</svg>
</div>
</body>
</html>

React编写SVG组件

在React中,React的jsx标签与HTML中的标签几乎是一一对应的,我们可以通过编写jsx来描述组件。所以不难想到,我们可以使用svg以及与其关联的jsx标签(譬如<path><g>等)来手写一个React的SVG组件:

export const IconComment = () => {
return (
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
width="200"
height="200">
<path
d="M256 32C114.6 32 0 125.1 0 240c0 47.6 19.9 91.2 52.9 126.3C38 405.7 7 439.1 6.5 439.5c-6.6 7-8.4 17.2-4.6 26S14.4 480 24 480c61.5 0 110-25.7 139.1-46.3C192 442.8 223.2 448 256 448c141.4 0 256-93.1 256-208S397.4 32 256 32zm0 368c-26.7 0-53.1-4.1-78.4-12.1l-22.7-7.2-19.5 13.8c-14.3 10.1-33.9 21.4-57.5 29 7.3-12.1 14.4-25.7 19.9-40.2l10.6-28.1-20.6-21.8C69.7 314.1 48 282.2 48 240c0-88.2 93.3-160 208-160s208 71.8 208 160-93.3 160-208 160z"/>
</svg>
);
}

这个IconComment就是一个普通的React组件,编写完成后我们就可以在需要使用的地方引入了:

效果如下:

SVG文件在React中的使用方式

组件模式使用

上面我们讲到了如何编写一个svg组件,但一般来说,我们都会让设计出svg资源,然后存放在项目某个目录下并进行使用。我们当然可以把设计出的svg的内容复制到我们的项目中,以组件的方式来使用:

但是每次都需要拷贝一个又一个的组件当然是一件很麻烦的事情,在webpack中我们使用svg资源的时候,其实更希望如同图片资源一样以模块的形式引入(import或者是require)并使用,就像下面一样:

如果要达到上面的目的,我们首先需要弄清楚一件事情,那就是咱们"import"的"IconAbc"到底是个什么?通过上面的代码反推,我们很容易回答,IconAbc肯定需要是一个React组件(函数组件或类组件)。

了解webpack的同学都知道,webpack可以通过loader,来处理一个资源在导入的时候会变成什么。但现在在webpack配置中,我们先不添加任何关于svg模块的处理loader,不出意外肯定会报错:

ERROR in ./src/icon-comment.svg 1:0

Module parse failed: Unexpected token (1:0)

You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders

译文:您可能需要适当的加载程序(loader)来处理此文件类型,目前没有配置加载程序来处理此文件。请参阅 https://webpack.js.org/concepts#loaders

问题我们已经很清楚了webpack无法找到处理svg模块的loader,那么现在的解决方案是什么呢?我们可以使用svgr提供的配合webpack的loader(Webpack - SVGR (react-svgr.com))就可以完成这个任务。

首先安装必要的依赖:yarn add -D @svgr/webpack

然后,配置webpack处理svg文件:

module.exports = {
... ...
module: {
rules: [
{
test: /\.tsx?/,
use: [
'babel-loader'
],
exclude: /node_moudles/
},
... ...
+ {
+ test: /\.svg$/,
+ use: ['@svgr/webpack']
+ }
]
},
... ...
}

完成配置以后,重新经过webpack编译打包,运行后会看到控制台的输出:

  • 效果1:我们通过console.log输出的IconComment是一个React组件纯函数。
  • 效果2:代码中我们使用<IconComment/>在屏幕上展示出来了。

PS:上图中import报错暂时可以不用关心,是IDE类型检查的语法提示,webpack打包是没有问题的,想要深入了解,可以参考:【长文详解】TypeScript与Babel、webpack的关系以及IDE对TS的类型检查 - 知乎 (zhihu.com)

回顾整个过程,我们可以用下面的图来描述这个过程:

资源模式使用

当然,我们有的时候并不想按照React组件的使用。例如,svg同样可以作为一些元素的背景,这个时候我们需要把svg是为类似于图片一样的资源,就像下面的方式:

如果svg的loader配置保持不变,还是@svgr/webpack,我们会看到没有起效果,并且,查看对应生成css样式文件,我们可以看到对应的url('./icon-comment.svg')被编译为了url(8ed4ed501566520a5cd0.svg)

这个8ed4ed501566520a5cd0.svg是什么呢?可能看起来还有点懵,我们尝试打包编译项目,看一下编译后的产物就知道了:

通过上图的结果可知,很明显svg在这种场景下依然被@svgr/webpack这个loader处理为了React组件,又因为咱们是在less/css中引用这个svg,loader内部将这种场景回退到了文件资源存放了。

现在,我们希望webpack在处理这种场景的时候,还是以普通资源的方式进行;同时,在React代码中依然能够将svg资源以组件的形式被引入。好在webpack支持这样的配置:

module.exports = {
... ...
module: {
rules: [
... ...
{
// 引用的资源如果是 '${svg-path}/icon-comment.svg?abc'
test: /\.svg$/,
resourceQuery: /abc/,
// 以webpack的资源形式加载(普通资源文件、base64等)
type: 'asset',
},
{
// 除了上面的匹配规则,我们都按照React组件来使用
test: /\.svg$/,
resourceQuery: {not: [/abc/]},
use: ['@svgr/webpack']
}
]
},
... ...
}

webpack5中的 type: "assets" 是什么?可以看这篇文章:

资源模块 | webpack 中文文档 (docschina.org)

在上述配置中,我们都将匹配svg资源的引用,不同的是,如果这个引用路径带上url query,则使用webpack5的asset资源模块来处理;否则,调用@svgr/webpack来将其转换为React组件。

完成上述的配置以后,我们适当的修改代码,如下所示:

关于关键代码的解释:

  1. index.tsx第三行和第四行我们均引入了./icon-comment.svg模块,不同的是第四行的引入路径我们还添加了与webpac配置中保持一致的url query = "abc"。同时,在下面我们分别打印了IconComment和IconCommentUrl。
  2. 在index.module.less中,.app样式中,我们添加的背景也使用./icon-comment.svg,也添加了url query = "abc"。

代码运行以后,我们首先从UI上能够看到效果:

其次,从控制台也能看到对应的IconComment就是React函数组件;IconComment是svg资源的base64 DataUrl:

demo地址

本文相关demo已提交至webpack5-react-demo的svg_use_case分支,供读者参考:

w4ngzhen/webpack5-react-demo at svg_use_case (github.com)

最新文章

  1. 解决mybatis foreach 错误: Parameter &#39;__frch_item_0&#39; not found
  2. java中注解的使用与实例(一)
  3. c++2008 并行配置文件和获取字典的所有key的方法
  4. mogodb监控脚本
  5. python 学习笔记6(函数)
  6. js——倒计时
  7. 主键、外键、超键、候选键的区别【Written By KillerLegend】
  8. 第三百四十天 how can I 坚持
  9. 用Python作GIS之三:入口程序 - stargui.py
  10. 通过ulimit改善linux系统性能(摘自IBM)
  11. UberX及以上级别车奖励政策(优步北京第二、三组)
  12. hibernate的常用配置
  13. Struts2+Spring+Hibernate step by step 03 整合Spring之中的一个(在DAO层验证username和password)
  14. java foreach实现原理
  15. Linux进程作业常用命令
  16. Integer 与 int
  17. 【Flask】微型web框架flask大概介绍
  18. Android 沉浸式状态栏完美解决方案
  19. 出错:Failed to convert property value of type &#39;org.apache.ibatis.session.defaults.DefaultSqlSessionFactory&#39; to required type &#39;java.lang.String&#39; for property &#39;sqlSessionFactoryBeanName&#39;;
  20. 痞子衡嵌入式:第一本Git命令教程(3)- 变动(status/diff)

热门文章

  1. 介绍一个jmeter录制脚本谷歌插件 —— metersphere-chrome-plugin
  2. Vue 基础学习总结
  3. EdgeCore初学习
  4. Froms
  5. 【GUI开发案例】用python爬百度搜索结果,并开发成exe桌面软件!
  6. vs同步配置
  7. MIUI12解决安装charles抓包安装证书后还是提示证书不安全
  8. svn 日常使用的错误集锦
  9. MySQL数据库下载以及启动软件的详细步骤
  10. React Server Component: 混合式渲染