探索前端项目中icon使用的最佳实践

1、前言

我们在网页开发中,无时无刻不在与 icon 打交道, 那么怎么样的实现方式才是最优的呢? 之前在开发中本着"省事"的目的,一直将就,开发过程中每次遇到icon的问题都比较头疼, 尤其是在icon频繁导出,代码重复编写上,着实是一件难忍的事。 实际上"省事"并不"省事", 相反非常头痛, 最近进行项目重构,于是着手将代码中icon的使用进行梳理, 重新进行替换

2、问题与诉求

本人在工作多年,一直使用icon的途中, 遇到过很多问题, 记录如下

  • 1、 icon 用图片png 还是 svg
  • 2、 图片用网络加载还是本地加载,然后打包到项目中
  • 3、 我想要体积更小,使用最方便
    • 我想要网络只加载一次
    • 我想打包体积更小
  • 4、 svg 在项目中的使用过程中, 如何优雅的使用
    • 我只想引入一次
    • 需要满足hover支持变色
    • 满足传入颜色、背景色
    • 可以通过一行代码搞定、包括父元素hover 也能让icon变色

3、解释

先简单解释一下上面的问题

1、 对于第一个问题: 图片和svg其实各有优势, svg是 矢量图,有不失真的优势, 是靠线条画出来的, 对于简单的侧重线条类的 icon 建议使用 svg, svg是一段代码,体积非常小, 但是对于颜色丰富、色彩绚丽,可能 图片 尤其是 png 格式的图片更加合适, 实际开发中,我们为了保证导出的图片不失真,通常导出 2倍图, 通过css控制实际宽高低于导出的宽高, 那么导致的问题就是图片体积又非常的大。 那么其实对于公司通用的icon来说,一般都是通过figma或者其他平台直接导出, 对于icon来说,可以和设计师一起商量, 设计出大小一致, 尺寸和常用色一致的 svg icon

2、对于第二个问题,其实也是比较取舍的, icon 通过网络加载,就会占用网络带宽, 如果用本地打包资源就会使打包后的体积增大, 通常会根据具体情况进行综合考虑。一些项目可能选择混合使用,将一些核心的、不经常变更的图片打包到项目中,而将一些可能经常更新或较大的图片通过网络加载

4、 方案

4-1、 react 中 svg icon 的使用 与 问题

js 复制代码
import svgr from "vite-plugin-svgr";
//...
plugins: [react(), svgr(), splitVendorChunkPlugin(), visualizer()],
//...

通常我们在react中加载svg 是需要在vite.config 中配置一下vite-plugin-svgr 使用时, 导入svg 像导入组件一样

js 复制代码
import { ReactComponent as IconCountry } from "./country.svg";

// use
<IconCountry className="icon"></IconCountry>

我们可以在icon中通过类的样式进行更改icon的大小, 但是对于icon的边框颜色和填充色,通常就不是那么容易了, 我们可能需要写出以下的代码

css 复制代码
.icon {
  width: 10px;
  height:10px;
    & > svg {
      width: 40px;
      height: 40px;
    }
    & > rect {
      fill: #5051ff;
    }
    & > path {
      stroke: #fff;
    }
  }

像代码写的,如果改svg的颜色,通过样式改通常要将里面的各种路径元素颜色都要更改,非常的不优雅, 这还是没有添加 hover , 添加 hover, 又要对内部 元素全部添加一遍, hover 变色可能还可以通过改样式解决, 那么active, 选中态呢? 增加更多逻辑交互后, 通常我们只能再多导入一个 变色后的icon,才能让代码没有那么恶心

4-2、 使用 react-svg-sprite-generator 解决方案

其实解决上面的问题, 思路就是我们需要通过工具先将所有的svg打包成精灵图, 进行压缩, 去掉本身的边框颜色和fill的颜色, 然后我们可以通过svg <use xlinkHref={{spriteUrl}#{name}} /> 的方式,再项目中找到对应的资源中的icon,并进行设置。 再npm中寻找这个工具的时候恰好找到了这个包, 地址 这个包的使用非常的简单

js 复制代码
npm i react-svg-sprite-generator -D
npx svgsprite --src ./icons --dest ./src/icons

可以写到package.json 中 每次 npm run svg 即可

只需要安装后, 在目录下的终端执行一下命令,即可打包出来一个 sprite.svg 的精灵图, 那么使用就很简单了

js 复制代码
 <svg
  style={{
    width: size + "px",
    height: size + "px",
  }}
  strokeWidth={strokeWidth}
  stroke={strokeColor}
>
  <use xlinkHref={`${spriteUrl}#${写入想展示的icon的名字(大写的)}`} />
</svg>

使用后发现颜色和大小都可以灵活控制了, 而且相比原始的 svg 文件, 打包出来的进行了压缩与处理, 原始的svg文件其实就不再需要了, 当然代码中不使用, 也不会打包到dist目录中, 那么build后的体积其实大幅降低了

那么为了更方便使用, 我们再进一步进行封装, 代码如下

js 复制代码
import * as IconNames from "./names";
import spriteUrl from "./sprite.svg";
import { useEffect, useState } from "react";
const SvgIcon = ({
  name,
  color = "#222222",
  hoverColor = "",
  size = 16,
  strokeWidth = 3,
  classNames,
  fill,
}: {
  name: keyof typeof IconNames;
  size?: number;
  color?: string;
  hoverColor?: string;
  strokeWidth?: number;
  classNames?: any;
  fill?: string;
}) => {
  const [strokeColor, setStrokeColor] = useState(color);
  const onMouseHover = () => {
    if (hoverColor) {
      setStrokeColor(hoverColor);
    }
  };
  const onMouseLeave = () => {
    if (hoverColor) {
      setStrokeColor(color);
    }
  };

  useEffect(() => {
    setStrokeColor(color);
  }, [color]);

  return (
    <svg
      style={{
        width: size + "px",
        height: size + "px",
      }}
      strokeWidth={strokeWidth}
      stroke={strokeColor}
      fill={fill || "none"}
      onMouseOver={onMouseHover}
      onMouseLeave={onMouseLeave}
      className={classNames}
    >
      <use xlinkHref={`${spriteUrl}#${name}`} />
    </svg>
  );
};
SvgIcon.IconNames = IconNames;
export default SvgIcon;

使用

js 复制代码
<SvgIcon name={IconNames.PRODUCT} color="#f00" hoverColor="#ff0" size={24} />

使用起来就非常简单了, 我们可以方便的控制颜色和尺寸了, 但是我们上面讲过, 能不能实现一行代码就实现样式的 hover, 在组件里 不进行 更新hover状态也能改变颜色呢 , 答案也是可以的, 那么就需要我们使用 tailwind CSS 通过 className的方式传入了

js 复制代码
<div className="group bg-red-100 hover:bg-red-300">
        <div className="group-hover:text-[#0f0]">111</div>
        <SvgIcon name={IconNames.PRODUCT} classNames="group-hover:stroke-[yellow]  group-hover:fill-[#f00]" size={24} />
 </div> 

如图: 就是我们通过 tailwindcss 的方式, 实现了hover到父元素,同时控制子元素中的文本和icon变色的例子, 到此,我们就基本实现了我们的需求, 代码也非常的简洁

5、总结

本文通过总结工作中遇到的icon使用的问题, 进行一步步分析拆解, 最终通过使用 npm 中的一个 svg 包,生成了压缩的合并svg的精灵图, 通过svg 的use 使用, 进一步封装成了组件, 然后通过使用 tailwind css 的方式,实现了一行代码即可灵活控制icon的hover变色、更改大小、边框等等需求, 在减少代码build体积、提高开发速度等方面成效显著, 可以说是一劳永逸

相关推荐
F-2H19 分钟前
C语言:指针4(常量指针和指针常量及动态内存分配)
java·linux·c语言·开发语言·前端·c++
WebDeveloper200126 分钟前
如何使用美国域名中心US Domain Center和WordPress创建商业网站
运维·服务器·css·网络·html
gqkmiss1 小时前
Chrome 浏览器插件获取网页 iframe 中的 window 对象
前端·chrome·iframe·postmessage·chrome 插件
m0_748247553 小时前
Web 应用项目开发全流程解析与实战经验分享
开发语言·前端·php
m0_748255024 小时前
前端常用算法集合
前端·算法
真的很上进4 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
web130933203984 小时前
vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法
前端·vue.js·elementui
NiNg_1_2344 小时前
Echarts连接数据库,实时绘制图表详解
前端·数据库·echarts
测试老哥4 小时前
外包干了两年,技术退步明显。。。。
自动化测试·软件测试·python·功能测试·测试工具·面试·职场和发展
如若1235 小时前
对文件内的文件名生成目录,方便查阅
java·前端·python