Skip to main content

React 使用 SVG 的姿势

前言#

在项目开发中,跟 svg 打交道可以说必不可少,想对比 png,svg 更可控,还能改变 svg 的样式,体积也会小很多,也相当于一种性能优化。下面看看 React 中使用 svg 的姿势。

svg 它是一种向量图的图片格式,即可伸缩向量图(Scalable Vetor Graphics)。在构建前端应用时经常使用一些图标,相对应我们会比较偏向于使用 svg,那么 svg 有什么好处呢,接下来会说到,这篇文章主要围绕在 React 中如何通过组件的方式使用 svg。

首先看看在项目中使用 svg 有什么好处?

  • 压缩后的文件体积小
  • 可以无损伸缩到任意尺寸(特别小的尺寸,需要一些 css 特性来解决),放大不会失真
  • 设计可控,比如交互和滤镜

那么下面会说说 React 开发中,如何组件化使用 SVG 的,会讲的实现方式有两种 :

  • 通过插件 webpack-iconfont-plugin-nodejs + 封装 Icon 组件实现;
  • Vite 构建下使用 vite-plugin-svgr 插件 直接导入实现;

实现方式#

封装 Icon 组件实现使用#

这种方式生成字体文件,然后再使用对应的类型来显示的。

首先需要安装依赖 webpack-iconfont-plugin-nodejs

yarn add webpack-iconfont-plugin-nodejs
# or
npm install webpack-iconfont-plugin-nodejs

再项目更目录创建 svg2font.js 文件

const WebpackIconfontPluginNodejs = require('webpack-iconfont-plugin-nodejs')const path = require('path')
const dir = 'src'
const options = {  fontName: 'xxx-icons', // 生成文件名称  cssPrefix: 'b-font', // className 使用的 svg 的前缀名称  svgs: path.join(dir, 'svg/*.svg'), // 获取 svg 的文件夹位置。  fontsOutput: path.join(dir, 'fonts/'), // 创建 fonts 文件夹存放下面输出的文件👇  cssOutput: path.join(dir, 'fonts/font.css'),  htmlOutput: path.join(dir, 'fonts/_font-preview.html'),  jsOutput: path.join(dir, 'fonts/fonts.js'),}
new WebpackIconfontPluginNodejs(options).build() // 执行并构建生成

这时候我们需要一只命令命令执行 svg2font.js 来生成对应的 fonts 文件。

在 package.json 中添加命令

"scripts": {  "svg": "rm -rf fonts; node svg2font.js;"},

将设计给的 svg 图标下载添加到 src/svg 文件夹 📁 下,执行 yarn svg ,生成字体文件。

有了字体文件,那么我们就可以通过类名去直接使用,比如 <i class=“b-font-{svg文件名称}” />

这样虽然可以但是不够优雅,我们可以封装成一个 Icon 组件来使用。

编写 Icon 组件

src/components/Icon/icon.tsx

import { FC, AnchorHTMLAttributes } from "react";import classNames from "classnames";
// React.AnchorHTMLAttributes<HTMLElement> 保留原生标签属性interface IconProps extends React.AnchorHTMLAttributes<HTMLElement> {  icon: string; // 存放 svg 文件下的 svg 名称  size?: string | number; // 图标大小}
const Icon: FC<IconProps> = ({ icon, size, className, ...restProps }) => {  return (    <i      style={{ fontSize: `${size}px` }}      className={classNames(`b-font-${icon}`, className)}      {...restProps}    />  );};
export default Icon;

使用

<Icon icon="wx" size={66} />

效果图

Untitled


Vite 构建下使用插件直接导入方式#

安装 vite-plugin-svgr 插件依赖

yarn add vite-plugin-svgr
# or
npm install vite-plugin-svgr

在 vite.config.ts 进行配置

import { defineConfig } from "vite";import react from "@vitejs/plugin-react";import svgr from "vite-plugin-svgr";
// https://vitejs.dev/config/export default defineConfig({  plugins: [react(), svgr()],});

重启 run dev ,使用的地方引用导入:

import { ReactComponent as ReactIcon } from "./svg/react.svg";
function App() {  const [count, setCount] = useState(0);  // console.log(SvgIcon);  return (      <div        style={{          background: "#F1F1FF",        }}      >        <ReactIcon />      </div>    </div>  );}
export default App;

在 ts 环境里,会提示 模块 ""*.svg"" 没有导出的成员 "ReactComponent” ,解决办法就是在vite-env.d.ts 文件里面添加 👇:

/// <reference types="vite-plugin-svgr/client" />

这样提示 ts 检查类型的错误就会消失。

或者在 ts.config.json 里面配置

{  "compilerOptions": {    // 省略其它配置    "types": ["vite-plugin-svgr/client"]  }}

二者其中一个配置了即可。

总结#

上面介绍了两种模式下使用 SVG ,如果是在 webpack 构建下,使用第一种方式,Vite 构建,直接用第二种方式比较方便便捷。对于上面两种方式,社区里面还有更多方案,仅供大家参考。