SVG转React组件全解析

前言

最近在公司里从0到1的搭建了一套SVG图标上传及图标组件发布系统,目的是让图标上传发布NPM包之间的全部流程实现自动化。

主要的流程为:

现在社区上利用SVG用作图标组件的UI库其实非常多,例如AntdMUI等等,但是如何自动化的将一个单色SVG或者多色SVG的文件转化为React组件的文章却属实不多。在上述的整个系统实现的过程中,其实有许多的功能点,或者踩过的坑都蛮有实战价值,所以简单的通过这篇文章来详细的记录和总结一下,希望能够帮助到相关的同学。

获取SVG文件内容

作为整个系统的起点,当用户在客户端将SVG图标文件上传后,我们在服务端需要正确的接收文件并解析出文件的内容。 这里,我使用了流的形式,去接收并获取传输过来的文件内容。

js 复制代码
const onReadFile = (file) => {
  return new Promise((resolve, reject) => {
    const fileStream = fs.createReadStream(file.filepath);
    let fileValue = "";
    fileStream.on("data", function (chunk) {
      // 读取内容
      fileValue += chunk;
    });
    fileStream.on("end", function (chunk) {
      // 读取内容
      resolve(fileValue);
    });
  });
};

解析SVG文件内容

利用Node原生提供的流的方法,我们可以很简单的获取到上传文件的内容。我们先来简单看下SVG内容的一些特点。

以一个单色SVG图标为例,我们可以看到在SVG的标签里面,除了我们熟悉的widthheightid之外,还有很多一些额外的属性,例如versionxmlns等等,通常来说这些属性对我们的元素构成并没有太大的影响。接着,我们还可以发现,SVG里其实是利用了path或者rect等线性或者图案元素去构成最终的图形,并利用fill或者stroke来填充颜色。还需要注意的是,SVG图形还会携带一个viewBox属性,这个属性实则是去控制该SVG图形的可视区域,对于SVG图形的展示至关重要,通常来说都需要保留。

html 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <title>Delete</title>
    <g id="图层1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="图层2" transform="translate(-564.000000, -283.000000)" fill-rule="nonzero">
            <g id="Delete" transform="translate(588.000000, 307.000002) rotate(0.010000) translate(-588.000000, -307.000002) translate(564.004188, 283.004189)">
                <rect id="矩形" fill-opacity="0" fill="#000000" x="0" y="0" width="47.9916246" height="47.9916261"></rect>
                <path d="M33.9971919,11.9979065 L43.9985715,11.9979065 L43.9985715,16.0003331 L39.996145,16.0003331 L39.996145,43.9985728 L7.99547965,43.9985728 L7.99547965,16.0003331 L4.00242651,16.0003331 L4.00242651,11.9979065 L14.0038061,11.9979065 L14.0038061,4.00242663 L33.9971919,4.00242663 L33.9971919,11.9979065 Z M35.9937185,16.0003331 L11.9979062,16.0003331 L11.9979062,39.9961462 L35.9937185,39.9961462 L35.9937185,16.0003331 Z M17.9968592,21.9992864 L21.9992857,21.9992864 L21.9992857,33.9971929 L17.9968592,33.9971929 L17.9968592,21.9992864 Z M25.9923389,21.9992864 L29.9947654,21.9992864 L29.9947654,33.9971929 L25.9923389,33.9971929 L25.9923389,21.9992864 Z M17.9968592,7.99547989 L17.9968592,11.9979065 L29.9947654,11.9979065 L29.9947654,7.99547989 L17.9968592,7.99547989 Z" id="形状" fill="#5C7080"></path>
            </g>
        </g>
    </g>
</svg>

我们再来看下多色图标,可以看到多色图标里,可能会含有更多我们不常见的标签属性,如title标签,和style的样式标签,另外,也会有defs标签和use属性,我们可以在defs标签内去定义可能需要多次用到的图形,然后再利用use其ID来做到图形的复用。

html 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<svg width="40px" height="40px" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <title>Title</title>
    <style type="text/css">
		.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#0C40A8;}
	</style>
    <defs>
        <pattern id="pattern-1" width="100%" height="100%" patternUnits="objectBoundingBox">
            <use xlink:href="#image-2"></use>
        </pattern>
        <image id="image-2" width="40" height="40" xlink:href="data:image/png;base64,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"></image>
    </defs>
    <g id="Icon" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g transform="translate(-232, -936)" id="编组-8">
            <g transform="translate(228, 932)">
                <rect id="矩形" x="0" y="0" width="48" height="48"></rect>
                <g id="Kirby-Yicon-glo_collected备份-3" transform="translate(4, 4)" fill="url(#pattern-1)" fill-rule="nonzero">
                    <g id="加载4_loading-four">
                        <path class="st0" d="M20,0 C31.0456996,0 40,8.95430038 40,20 C40,31.0456996 31.0456996,40 20,40 C8.95430038,40 0,31.0456996 0,20 C0,19.1715729 0.671572875,18.5 1.5,18.5 C2.32842712,18.5 3,19.1715729 3,20 C3,29.3888454 10.6111546,37 20,37 C29.3888454,37 37,29.3888454 37,20 C37,10.6111546 29.3888454,3 20,3 C19.1715729,3 18.5,2.32842712 18.5,1.5 C18.5,0.671572875 19.1715729,0 20,0 Z" id="路径"></path>
                    </g>
                </g>
            </g>
        </g>
    </g>
</svg>

完成React组件的转化

在了解完SVG的一些属性特点之后,我们便开始利用Node脚本来完成SVG文件到React组件的转化。

但是在此之前,我们还得清晰的知道,我们最终需要得到的是一个怎样的React组件,也就是我们需要去理清楚这个组件需要具有怎样的功能点,作为一个图标组件,我们需要做到:

  • 动态控制大小,或者宽高
  • 动态控制颜色
  • 支持传入常见一些属性和事件

所以,我们的模版代码大概如下:

typescript 复制代码
import React from "react";

export interface IconProps extends React.SVGProps<SVGSVGElement> {
  fontSize: string;
  color?: string;
}

export const DeleteIcon: React.FC<IconProps> = (props) => {
  const { fontSize, color, width, height, ...rest } = props;

  return (
    <svg
      fill={color}
      width={fontSize || width}
      height={fontSize || height}
      {...rest}
    >
      <path
        d="M33.9971919,11.9979065 L43.9985715,11.9979065 L43.9985715,16.0003331 L39.996145,16.0003331 L39.996145,43.9985728 L7.99547965,43.9985728"
      ></path>
    </svg>
  );
};

针对单色图标,我们可以简单的增加fontSizecolor来让调用方去控制图标的大小和颜色。需要注意的是,由于SVG内会存在多个path或者其他的一些图形,这些图形可能在设计的时候就已经被定义好了颜色,所以我们需要在转化组件之前,将图形里的fill或者stroke属性全部去除掉,这样才能统一的在顶层通过fill属性来控制图标的颜,另外,为了更好的控制图形的大小,我们也需要把图形原来的widthheight给去掉,最后再在顶层注入我们模版代码里的fontSize变量。还需要注意的时候,SVG图形里的诸多属性,如果直接代入到React组件里面是会报错的,例如xmlns:link等等,我们也需要将这些属性给去除掉。

怎么去掉呢?我们可以利用一个强大的SVG工具,SVGO(github.com/svg/svgo),他...%25EF%25BC%258C%25E4%25BB%2596%25E9%2587%258C%25E9%259D%25A2%25E5%258C%2585%25E5%2590%25AB%25E4%25BA%2586%25E5%25BC%25BA%25E5%25A4%25A7%25E7%259A%2584%25E5%25A4%2584%25E7%2590%2586SVG%25E5%2590%2584%25E7%25A7%258D%25E5%25B1%259E%25E6%2580%25A7%25E7%259A%2584%25E9%259C%2580%25E6%25B1%2582%25EF%25BC%258C%25E5%2585%25B6%25E4%25B8%25AD%25E5%25B0%25B1%25E5%258C%2585%25E5%2590%25AB%25E4%25BA%2586%25E6%2588%2591%25E4%25BB%25AC%25E4%25B8%258A%25E8%25BF%25B0%25E7%259A%2584%25E5%258A%259F%25E8%2583%25BD%25EF%25BC%259A "https://github.com/svg/svgo)%EF%BC%8C%E4%BB%96%E9%87%8C%E9%9D%A2%E5%8C%85%E5%90%AB%E4%BA%86%E5%BC%BA%E5%A4%A7%E7%9A%84%E5%A4%84%E7%90%86SVG%E5%90%84%E7%A7%8D%E5%B1%9E%E6%80%A7%E7%9A%84%E9%9C%80%E6%B1%82%EF%BC%8C%E5%85%B6%E4%B8%AD%E5%B0%B1%E5%8C%85%E5%90%AB%E4%BA%86%E6%88%91%E4%BB%AC%E4%B8%8A%E8%BF%B0%E7%9A%84%E5%8A%9F%E8%83%BD%EF%BC%9A")

js 复制代码
 const result = optimize(fileValue, {
   plugins: [
     "cleanupAttrs",
     "removeComments",
     "removeXMLProcInst",
     "removeDoctype",
     "removeUselessStrokeAndFill",
     "removeStyleElement",
     "removeTitle",
     {
       name: "removeAttrs",
       params: {
         attrs:
           "(width|height|xmlns|stroke|fill|class|version|fill-rule|xmlns.*|xml.*)",
       },
     },
     {
       name: "mergePaths",
       params: {
         force: true,
       },
     },
   ],
 });

上述代码中,我们就可以快速的清除掉众多我们并不需要的属性。在拿到我们一个相对干净的SVG代码之后,我们再把利用正则把核心的SVG代码和本身的viewBox给提取出来。

js 复制代码
  // 利用正则提取viewBox
  const viewBox = svgValue.match(/(?<=viewBox=").*?(?=\")/).length
    ? res.match(/(?<=viewBox=").*?(?=\")/)[0]
    : "0 0 1024 1024";
    
const createComponent = (name, viewBox, svg) => {
  const _svg = svg.match(/<svg.*?<\/svg>/gi)
  // 提取svg标签包裹的内容。
  const svgContent = _svg[0].substring(_svg[0].indexOf('>') + 1).split('</svg>')[0]
  const component = `
    export const ${name} = (props: IconProps) => {
      const { fontSize, color, width, height, ...rest } = props;
      return (
        <svg
        
          viewBox="${viewBox}"         
          fill={color}
          width={fontSize || width}
          height={fontSize || height}
          {...rest}
        >
          ${svgContent}
        </svg>
      )
    }
  `
  return component;

};

这样,一个简单的单色SVG组就转化完成了。

完成多色图标的换色功能

但是,如果我们把功能点再扩展一下,假设UI同学上传了一个双色图标或者一个多色图标,我们还能不能利用类似的代码去实现一个多色图标组件,并且支持多色图标的换色呢? 其实也是可以的,而且实现起来也不难。 以一个双色图标为例:

html 复制代码
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
  <path fill="#D9D9D9" d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zm0-448H184V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136z"/>
  <path fill="#D9D9D9" d="M712 304c0 4.4-3.6 8-8 8h-56c-4.4 0-8-3.6-8-8v-48H384v48c0 4.4-3.6 8-8 8h-56c-4.4 0-8-3.6-8-8v-48H184v136h656V256H712v48z"/>
  <path fill="#cccccc" d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zm0-448H184V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136z"/>
</svg>

我们可以看到,这里存在三个path路径,并且都填充了两种颜色,分别是#D9D9D9#cccccc,那么,我们可以非常容易的想到,只要把将用到这两个颜色的地方全部都用变量去代替,让调用方用入参的形式去控制他们的值不就可以做到双色图标的换色了吗,换言之,任意一个多色图标都可以用这种方法去做到换色,区别只在于控制颜色的多少罢了。

js 复制代码
const transFormSvgColor = (svg, mainColor, subColor) => {
  if (mainColor) {
    svg = svg.replace(
      new RegExp(`"${mainColor}"`, "g"),
      `{mainColor || '${mainColor}'}`
    ); // 带入主色
  }
  if (subColor) {
    svg = svg.replace(
      new RegExp(`"${subColor}"`, "g"),
      `{subColor || '${subColor}'}`
    ); // 带入副色 //
  }
  return svg;
};

代码其实非常容易理解,以双色图标为例,我们在组件模版代码上新增两个mainColorsubColor的入参,这两个入参的默认值就是该双色图标原来的颜色,如上述例子里的#D9D9D9#cccccc,然后将svg里面用到这两个颜色的地方,全部换成变量。 所以,针对双色图标,我们的模版代码则是:

ini 复制代码
const createTwoToneComponent = (name, viewBox, svg) => {
  const _svg = svg.match(/<svg.*?<\/svg>/gi)
  // 提取svg标签包裹的内容。
  const svgContent = _svg[0].substring(_svg[0].indexOf('>') + 1).split('</svg>')[0]
  const component = `
    export const ${name} = (props: IconProps) => {
      const { fontSize, color, width, height, mainColor, subColor ...rest } = props;
      return (
        <svg
          viewBox="${viewBox}"         
          width={fontSize || width}
          height={fontSize || height}
          {...rest}
        >
          ${svgContent}
        </svg>
      )
    }
  `
  return component;

};

这样,我们就可以完成双色图标的换色功能了。

React属性处理

在完成组件的转化之后,当我们在开发环境去调用这个组件的时候,会发现控制台可能会报出许多警告,有时候甚至会弹出许多报错。为什么呢?主要是因为:

  • React里的标签属性需要变成小驼峰命名,而Svg本身的属性名并不是。
  • 原生标签里的class需要转化为className
  • React组件不支持内置标签。

针对第一和第二点,我们仅需要利用正则去全局替换掉即可:

js 复制代码
/**
 * 适配React 将参数转为驼峰命名
 * @param {*} svgString
 * @returns
 */
const parseReactApi = async (svgString) => {
  const reactApiConfig = {
    "xlink:href": "href",
    "stroke-width": "strokeWidth",
    "fill-rule": "fillRule",
    "fill-opacity": "fillOpacity",
    "stop-color": "stopColor",
    "class": "className"
  }
  if (reactApiConfig) {
    Object.keys(reactApiConfig).forEach((key) => {
      res = res.replace(new RegExp(`${key}`, "g"), `${reactApiConfig[key]}`);
    });
  }

  return res;
};

而对于第三点,我们并不能简单粗暴的直接将给删除掉,因为style标签里的内容是则是对某些图层的样式定义,如果直接去掉的话,图层的样式就会发生改变。怎么解决呢?其实很简单,我们只需要将style标签的样式转化为行内样式就可以了,利用SVGO就可以很简单的做到。

js 复制代码
 const result = optimize(fileValue, {
   plugins: [
       "inlineStyles",
   ],
 });

避免图层彼此影响

如果经常使用复杂的SVG多色图标的话,很容易注意到一个问题,就是图形之间的复用,在带来便利的同时,也会带来诸多问题。 我们来看个例子:

html 复制代码
<svg width="56px" height="56px" viewBox="0 0 56 56" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <defs>
        <linearGradient x1="11.4130931%" y1="18.2153362%" x2="79.6703028%" y2="79.9212994%" id="custom-1">
            <stop stop-color="#FFBC00" offset="0%"></stop>
            <stop stop-color="#FF9400" offset="100%"></stop>
        </linearGradient>
    </defs>
    <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g  transform="translate(-435.000000, -626.000000)">
            <g transform="translate(435.000000, 626.000000)">
                    <use id="形状结合" fill="url(#custom-1)"></use>

            </g>
        </g>
    </g>
</svg>

<svg width="56px" height="56px" viewBox="0 0 56 56" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <defs>
        <linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="custom-1">
            <stop stop-color="#FFFFFF" stop-opacity="0.561325393" offset="0%"></stop>
            <stop stop-color="#FFFFFF" stop-opacity="0" offset="100%"></stop>
        </linearGradient>
    </defs>
    <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g  transform="translate(-435.000000, -626.000000)">
            <g transform="translate(435.000000, 626.000000)">
                    <use id="形状结合" fill="url(#custom-1)"></use>

            </g>
        </g>
    </g>
</svg>

可以看到,上述的两个SVG元素,都在defs里定义了一个可复用的图形,并且ID都是custom-1,这样的话,如果他们在不同的页面单独使用的时候,其实不会有任何问题。但是如果他们同时出现在一个页面内,后出现的SVG在寻找图层的时候,就会找到先出现的SVG里的ID为custom-1的图层。换句话来说,图层的寻找是整个页面内从上到下的,如果两个SVG元素的内定义的图形ID一致,就会导致图形复用时出现偏差,随之出现问题。

怎么解决呢?其实很简单,我们只需要保证在生成组件时,组件内的图层ID都是唯一的就好了,我们还是可以利用SVGO去实现这个功能。

js 复制代码
    // 生成随机id
    const randomId = () => {
      return Math.random().toString(36).substr(2, 8);
    };
  const result = optimize(svg, {
    plugins: [
      {
        name: "prefixIds",
        params: {
          prefix: randomId() + SvgComponentName,
        },
      },
    ],
  });

我们可以生成一个随机ID,拼接上我们的图标名称,再作为前缀拼接到到每个图层ID的前面就好了。

相关推荐
&白帝&1 小时前
Vue.js 过渡 & 动画
前端·javascript
总是学不会.1 小时前
SpringBoot项目:前后端打包与部署(使用 Maven)
java·服务器·前端·后端·maven
Fanfffff7202 小时前
深入探索Vue3组合式API
前端·javascript·vue.js
光影少年2 小时前
node配置swagger
前端·javascript·node.js·swagger
昱禹2 小时前
关于CSS Grid布局
前端·javascript·css
啊QQQQQ2 小时前
HTML:相关概念以及标签
前端·html
就叫飞六吧3 小时前
vue2和vue3全面对比
前端·javascript·vue.js
Justinc.3 小时前
CSS基础-盒子模型(三)
前端·css
qq_2518364573 小时前
基于ssm vue uniapp实现的爱心小屋公益机构智慧管理系统
前端·vue.js·uni-app
._Ha!n.3 小时前
Vue基础(二)
前端·javascript·vue.js