React18项目如何封装SVG组件

1,372 阅读1分钟

🌏 GitHub

React18 + webapck5

项目Demo:  feat: ✨ 封装全局SVG组件

🗂 svg-sprite-loader

官方 svg-sprite-loader

安装

npm
npm install svg-sprite-loader -D

yarn
yarn add svg-sprite-loader -D

创建SvgIcon组件

  • 路径:src/components/SvgIcon/index.tsx
import React, {FC} from 'react';
import './index.less';

interface SvgIconProps {
    svgName: string; // svg名字
    svgClass?: string; // 自定义类名
    color?: string; // 填充颜色
}

const SvgIcon: FC<SvgIconProps> = (props) => {
    const {svgName, color, svgClass} = props;
    return (
        <i aria-hidden='true'>
            <svg className={`svg-class ${svgClass}`}>
                <use xlinkHref={'#icon-' + svgName} fill={color} />
            </svg>
        </i>
    );
};

export default SvgIcon;

  • 路径:src/components/SvgIcon/index.less
.svg-class {
    display: inline-block;
    width: 1em;
    min-width: 14px;
    height: 1em;
    overflow: hidden;
    font-size: 50px;
}

📲 增加svg统一出口

Xnip2023-03-06_17-19-28.jpg

const importAll = (requireContext: __WebpackModuleApi.RequireContext) => requireContext.keys().forEach(requireContext);
try {
    importAll(require.context('./svg', true, /\.svg$/));
} catch (error) {
    console.log(error);
}
export {}; // 默认导出,ts如若不导出,会警告

  • 测试 my.svg
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1560995527910" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2159" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M376.672 285.248c0-82.702-69.85-149.805-156.116-149.805-86.148 0.06-156.056 67.103-156.056 149.805 0 40.25 16.597 76.809 43.521 103.78-7.343 29.532-11.164 60.255-11.164 91.931 0 142.423 78.148 267.398 195.518 337.419l31.223-123.012c-63.76-51.324-104.356-128.25-104.356-214.347 0-15.66 1.314-31.021 3.941-45.906 85.072-1.43 153.489-67.936 153.489-149.865z m133.906-83.656c53.97 0 104.416 14.052 147.818 38.643-6.866 16.85-10.508 35.129-10.508 54.242 0 82.761 69.849 149.805 155.996 149.805 86.148 0 156.116-67.103 156.116-149.805s-69.968-149.805-156.116-149.805c-20.179 0-39.521 3.691-57.133 10.36C679.708 110.257 598.457 84 510.578 84c-59.461 0-115.997 12.086-167.1 33.759l71.341 99.255c29.969-10.003 62.208-15.422 95.759-15.422z m290.858 291.93c-4.656 101.815-66.207 189.519-154.862 234.591-25.79-48.526-78.386-81.749-139.041-81.749-86.267 0-156.116 67.103-156.116 149.805 0 82.702 69.848 149.864 156.116 149.864 64.655 0 120.296-37.809 143.817-91.633 159.162-55.313 272.83-201.666 272.83-373.381 0-8.812-0.358-17.505-0.955-26.139l-121.789 38.642zM376.672 285.248c0-82.702-69.85-149.805-156.116-149.805-86.148 0.06-156.056 67.103-156.056 149.805 0 40.25 16.597 76.809 43.521 103.78-7.343 29.532-11.164 60.255-11.164 91.931 0 142.423 78.148 267.398 195.518 337.419l31.223-123.012c-63.76-51.324-104.356-128.25-104.356-214.347 0-15.66 1.314-31.021 3.941-45.906 85.072-1.43 153.489-67.936 153.489-149.865z m133.906-83.656c53.97 0 104.416 14.052 147.818 38.643-6.866 16.85-10.508 35.129-10.508 54.242 0 82.761 69.849 149.805 155.996 149.805 86.148 0 156.116-67.103 156.116-149.805s-69.968-149.805-156.116-149.805c-20.179 0-39.521 3.691-57.133 10.36C679.708 110.257 598.457 84 510.578 84c-59.461 0-115.997 12.086-167.1 33.759l71.341 99.255c29.969-10.003 62.208-15.422 95.759-15.422z m290.858 291.93c-4.656 101.815-66.207 189.519-154.862 234.591-25.79-48.526-78.386-81.749-139.041-81.749-86.267 0-156.116 67.103-156.116 149.805 0 82.702 69.848 149.864 156.116 149.864 64.655 0 120.296-37.809 143.817-91.633 159.162-55.313 272.83-201.666 272.83-373.381 0-8.812-0.358-17.505-0.955-26.139l-121.789 38.642zM376.672 285.248c0-82.702-69.85-149.805-156.116-149.805-86.148 0.06-156.056 67.103-156.056 149.805 0 40.25 16.597 76.809 43.521 103.78-7.343 29.532-11.164 60.255-11.164 91.931 0 142.423 78.148 267.398 195.518 337.419l31.223-123.012c-63.76-51.324-104.356-128.25-104.356-214.347 0-15.66 1.314-31.021 3.941-45.906 85.072-1.43 153.489-67.936 153.489-149.865z m133.906-83.656c53.97 0 104.416 14.052 147.818 38.643-6.866 16.85-10.508 35.129-10.508 54.242 0 82.761 69.849 149.805 155.996 149.805 86.148 0 156.116-67.103 156.116-149.805s-69.968-149.805-156.116-149.805c-20.179 0-39.521 3.691-57.133 10.36C679.708 110.257 598.457 84 510.578 84c-59.461 0-115.997 12.086-167.1 33.759l71.341 99.255c29.969-10.003 62.208-15.422 95.759-15.422z m290.858 291.93c-4.656 101.815-66.207 189.519-154.862 234.591-25.79-48.526-78.386-81.749-139.041-81.749-86.267 0-156.116 67.103-156.116 149.805 0 82.702 69.848 149.864 156.116 149.864 64.655 0 120.296-37.809 143.817-91.633 159.162-55.313 272.83-201.666 272.83-373.381 0-8.812-0.358-17.505-0.955-26.139l-121.789 38.642z" p-id="2160"></path></svg>

Xnip2023-03-06_17-05-22.jpg

  • 如果出现如果出现以上报错。说明项目环境TypeScript不识别webpack,需要安装@types/webpack-env依赖识别webpack类型文件。没有报错则忽略。
yarn add --dev @types/webpack-env

可以通过 require.context() 函数来创建自己的 context。可以给这个函数传入三个参数:一个要搜索的目录,一个标记表示是否还搜索其子目录, 以及一个匹配文件的正则表达式。Webpack 会在构建中解析代码中的 require.context() 。

⚙️ webpack配置

增加loader

  • 注意:新添加svg-sprite-loader放到test: /\.(eot|svg|ttf|woff|woff2?)$/之后,loader是从后往前执行
{
    test: /\.(eot|ttf|woff|woff2?)$/,
    exclude: path.resolve(__dirname, '../src/assets/icons'), // 不处理 svg类型文件
    type: 'asset/resource'
},
{
    test: /\.svg$/,
    loader: 'svg-sprite-loader',
    include: path.resolve(__dirname, '../src/assets/icons'),  
    options: {
        symbolId: 'icon-[name]' // symbolId和use使用的名称对应 <use xlinkHref={"#icon-" + svgName} />
    }
}

使用SvgIcon

引入

  • index.tsx 入口文件引入
  • import '@/assets/icons/index';
import React from 'react';
import ReactDOM from 'react-dom/client';
import {BrowserRouter} from 'react-router-dom';
import '@/assets/icons/index';  // 入口文件引入
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLDivElement);
root.render(
    <BrowserRouter>
        <App />
    </BrowserRouter>
);

组件使用

import React from 'react';
import {observer} from 'mobx-react-lite';
import {useStores} from '@/store';
import SvgIcon from '@/components/SvgIcon';
import './index.less';

const HomeOne = () => {
    const {globalStore, aboutStore} = useStores();
    const {count, name} = globalStore;
    const {count: aboutCount} = aboutStore;

    return (
        <div className='home-one-root'>
            <SvgIcon svgName='my' color='pink' svgClass='icon-my' />
            <SvgIcon svgName='loop' color='#1db02e' />
            <SvgIcon svgName='loading' color='#1db02e' />
            HomeOne
            <p>{name}</p>
            <p> globalStore: {count}</p>
            <p> aboutStore: {aboutCount}</p>
        </div>
    );
};
export default observer(HomeOne);
.home-one-root {
    display: flex;
    flex: 1;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    height: 800px;
    font-size: 50px;
    background: #c6e2ff;
    border-radius: 10px;

    .icon-my {
        font-size: 100px;
    }
}