本文主要讨论基于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)

一文详解如何在基于webpack5的react项目中使用svg的更多相关文章

  1. 一文详解Hexo+Github小白建站

    作者:玩世不恭的Coder时间:2020-03-08说明:本文为原创文章,未经允许不可转载,转载前请联系作者 一文详解Hexo+Github小白建站 前言 GitHub是一个面向开源及私有软件项目的托 ...

  2. Java网络编程和NIO详解9:基于NIO的网络编程框架Netty

    Java网络编程和NIO详解9:基于NIO的网络编程框架Netty 转自https://sylvanassun.github.io/2017/11/30/2017-11-30-netty_introd ...

  3. 一文详解 Linux 系统常用监控工一文详解 Linux 系统常用监控工具(top,htop,iotop,iftop)具(top,htop,iotop,iftop)

    一文详解 Linux 系统常用监控工具(top,htop,iotop,iftop)     概 述 本文主要记录一下 Linux 系统上一些常用的系统监控工具,非常好用.正所谓磨刀不误砍柴工,花点时间 ...

  4. 详解BOM用途分类及在汽车企业中的应用

    摘要:在整车企业中,信息系统的BOM是联系CAD.CAPP.PDM和ERP的纽带,按照用途划分产品要经过产品设计,工程设计.工艺制造设计.生产制造4个阶段,相应的在这4个过程中分别产生了名称十分相似但 ...

  5. 基于 React 封装的高德地图组件,帮助你轻松的接入地图到 React 项目中。

    react-amap 这是一个基于 React 封装的高德地图组件,帮助你轻松的接入地图到 React 项目中. 文档实例预览: Github Web | Gitee Web 特性 ️ 自动加载高德地 ...

  6. 一文详解 OpenGL ES 3.x 渲染管线

    OpenGL ES 构建的三维空间,其中的三维实体由许多的三角形拼接构成.如下图左侧所示的三维实体圆锥,其由许多三角形按照一定规律拼接构成.而组成圆锥的每一个三角形,其任意一个顶点由三维空间中 x.y ...

  7. 一文详解 WebSocket 网络协议

    WebSocket 协议运行在TCP协议之上,与Http协议同属于应用层网络数据传输协议.WebSocket相比于Http协议最大的特点是:允许服务端主动向客户端推送数据(从而解决Http 1.1协议 ...

  8. 1.3w字,一文详解死锁!

    死锁(Dead Lock)指的是两个或两个以上的运算单元(进程.线程或协程),都在等待对方停止执行,以取得系统资源,但是没有一方提前退出,就称为死锁. 1.死锁演示 死锁的形成分为两个方面,一个是使用 ...

  9. 一文详解Redis键过期策略

    摘要:Redis采用的过期策略:惰性删除+定期删除. 本文分享自华为云社区<Redis键过期策略详解>,作者:JavaEdge. 1 设置带过期时间的 key # 时间复杂度:O(1),最 ...

  10. 一文详解JackSon配置信息

    背景 1.1 问题 Spring Boot 在处理对象的序列化和反序列化时,默认使用框架自带的JackSon配置.使用框架默认的,通常会面临如下问题: Date返回日期格式(建议不使用Date,但老项 ...

随机推荐

  1. .net core Blazor+自定义日志提供器实现实时日志查看器

    场景 我们经常远程连接服务器去查看日志,比较麻烦,如果直接访问项目的某个页面就能实时查看日志就比较奈斯了,花了1天研究了下.net core 日志的原理,结合blazor实现了基本效果. 实现原理 自 ...

  2. 基于LZO的高性能无损数据解压缩IP

    LZOAccel-D LZO Data Decompression Core/无损数据解压缩IP Core LZOAccel-D是一个无损数据解压缩引擎的FPGA硬件实现,兼容LZO 2.10标准. ...

  3. 20_Vue如何监测数组类型数据发生改变的?

    通过上一节,我们知道了vue检测对象数据发生改变的原理 但是还有个api我们没有讲解,Vue.set(): 这个API比较适合在理解了对象检测的原理后进行讲解 案例准备 html <!-- 创建 ...

  4. 如何使用vscode快速配置C语言环境(简单实用)

    需要用到的工具: VSCode(Visual Studio Code) 一.首先打开官网链接,然后根据自己的电脑选择合适的安装程序进行下载. 二.在安装时默认点击下一步,最后记得勾选上添加path到系 ...

  5. 手记系列之二 ----- 关于IDEA的一些使用方法经验

    前言 本篇文章主要介绍的关于本人在使用IDEA的一些使用方法,一些常用设置,一些插件推荐和使用.请注意,本文特长,2w多字加上几十张图片,建议收藏观看~ 前提准备 idea官网: https://ww ...

  6. JUC学习笔记——进程与线程

    JUC学习笔记--进程与线程 在本系列内容中我们会对JUC做一个系统的学习,本片将会介绍JUC的进程与线程部分 我们会分为以下几部分进行介绍: 进程与线程 并发与并行 同步与异步 线程详解 进程与线程 ...

  7. 安卓APP和小程序渗透测试技巧总结

    安卓APP和小程序渗透测试技巧总结 免责声明: 安卓7以上抓取https流量包 证书信任 首先安装OpenSSL,此步骤不再赘述,可以参考百度. 然后安装模拟器(我使用的是夜神模拟器). 导出需要的证 ...

  8. Containerd 如何配置 Proxy?

    前言 在某些 air gap 场景中,往往需要离线或使用代理 (Proxy), 例如: 需要通过 Proxy pull 容器镜像: Docker Hub: docker.io Quay: quay.i ...

  9. Goland环境中Go module配置

    [现象] 从go vendor切换到go module之后,import包解析有问题.如下所示: 对应的go modules也没解析出来 [原因] 有两点原因: goland中go module配置存 ...

  10. Web浏览器Linux Shell(shellinabox解决通用区服务器Linux Shell访问很麻烦的问题)

    问题背景 通用区服务器的Linux Shell访问,比较麻烦 需要动态密码(手机上装Token)连跳板机,再用跳板机上的终端工具连Linux Shell 改进方法 使用shellinabox,就能直接 ...