手把手教你搭建 React UI 组件库 (二) 实现 Icon 组件(下)

365 阅读1分钟

1. 目标一: 我要为我的 icon 添加 css 样式

修改文件:@/lib/Icon/index.tsx

import * as React from 'react';
import './importAllIcons';
import './icon.scss'


interface IconType {
    icon: string
}

const Icon: React.FC<IconType> =({icon})=>{

    return<svg className='sweetui-icon'>
            <use xlinkHref={ `#${icon}` }></use>
        </svg>

}
export default Icon;

新建scss 文件, @/lib/Icon/icon.scss:

.sweetui-icon{
  width: 1em;
  height: 1em;
}

重新运行项目yarn start, 命令行输出错误:

image.png

错误原因: webpack 无法识别scss 类型文件, 需要安装专用的加载器

配置webpack, 告诉它如何加载使用scss文件

            {
                test: /\.scss$/,
                use: ['style-loader', 'css-loader', 'sass-loader']
            }

image.png

为什么需要配置这三个loader: 查看这篇博客

安装相关loader, 修改@/package.json,

    "sass-loader": "^7.1.0",
    "style-loader": "^0.23.1",
    "node-sass": "npm:sass",
    "css-loader": "^2.1.1",

image.png

运行安装命令: yarn

重新启动项目: yarn start 浏览器查看:

image.png

我们的目标成功了!!

2. 目标二: 我要给Icon添加点击事件

  1. 修改@/lib/Icon/index.tsx
import * as React from 'react';
import './importAllIcons';
import './icon.scss'


interface IconType {
    icon: string
    onClick?: ((event: React.MouseEvent<SVGSVGElement, MouseEvent>) => void) | undefined
}

const Icon: React.FC<IconType> =({icon, onClick})=>{

    return<svg className='sweetui-icon' onClick={onClick}>
            <use xlinkHref={ `#${icon}` }></use>
        </svg>

}
export default Icon;

技巧: 如何找到 onClick 类型? 查看这篇博文

修改@/lib/index.tsx:

import React from 'react';
import ReactDOM from 'react-dom';
import Icon from './Icon';


const fn =(e: React.MouseEvent<SVGSVGElement>)=>{
    console.log('haha',e.currentTarget);
}

ReactDOM.render(
    <div>
        <Icon icon='wechat' onClick={fn}/>
        <Icon icon='alipay'/>
    </div>
    ,
    document.getElementById('root')
)

浏览器测试:

image.png

我们的目标成功了!

3. 目标三: 一次性让我们的icon能响应事件

上个目标我们成功添加了 onClick事件!

如果我想为我的Icon组件添加, onClick, onMouseMove, onMouseLeave ...等属性, 我总不能一个个自己添加把?!

一次性添加所有响应事件的方法如下:

修改: @/lib/Icon/index.tsx

import * as React from 'react';
import './importAllIcons';
import './icon.scss'


interface IconType extends React.SVGAttributes<SVGSVGElement>{
    icon: string
}

const Icon: React.FC<IconType> =(props)=>{

    return<svg
        className='sweetui-icon'
        {...props}
    >
            <use xlinkHref={ `#${props.icon}` }></use>
        </svg>

}
export default Icon;

如何快速找到需要继承的父类 React.SVGAttributes<SVGSVGElement>看这篇博客

修改:@/lib/index.tsx

import React from 'react';
import ReactDOM from 'react-dom';
import Icon from './Icon';


const fn =(e: React.MouseEvent<SVGSVGElement>)=>{
    console.log('haha',e.currentTarget);
}

ReactDOM.render(
    <div>
        <Icon icon='wechat'
              onClick={fn}
              onMouseMove={()=>{console.log('move');}}
        />
        <Icon icon='alipay'/>
    </div>
    ,
    document.getElementById('root')
)

看浏览器:

image.png

我们的目标成功了!

4. 目标四: 解决className 会被覆盖的bug

@/lib/Icon/index.tsx部分代码如下, 如果props包含 className这个属性, 就会把className='sweetui-icon'覆盖掉了

const Icon: React.FC<IconType> =(props)=>{

    return<svg
        className='sweetui-icon'
        {...props}
    >
            <use xlinkHref={ `#${props.icon}` }></use>
        </svg>

}

解决办法:

安装classnames : yarn add classnames

修改@/lib/Icon/index.tsx

import * as React from 'react';
import './importAllIcons';
import './icon.scss'
import classNames from 'classnames';


interface IconType extends React.SVGAttributes<SVGSVGElement>{
    icon: string
}

const Icon: React.FC<IconType> =(props)=>{

    const {className, ...restProps} = props
    return<svg
        className={classNames('sweetui-icon', className)}
        {...restProps}
    >
            <use xlinkHref={ `#${props.icon}` }></use>
        </svg>

}
export default Icon;

我们的目标达成了!