1、需求分析
一个Button分以下几个不同的维度
- 不同的Button Type
- Primary
- Default
- Danger
- LinkButton
- 等等
- 不同的Button Size
- Normal
- Small
- Large
- 状态
定义props类型
interface BaseButtonProps {className?: string;/**设置 Button 的禁用 */disabled?: boolean;/**设置 Button 的尺寸 */size?: ButtonSize;/**设置 Button 的类型 */btnType?: ButtonType;children: React.ReactNode;href?: string;}
4、逻辑编写
我们需要注意以下几点:
我们来写第一版代码:
import React, { FC } from "react";import classNames from "classnames";export type ButtonSize = "lg" | "sm";export type ButtonType = "primary" | "default" | "danger" | "link";interface BaseButtonProps {/**设置 Button 的禁用 */disabled?: boolean;/**设置 Button 的尺寸 */size?: ButtonSize;/**设置 Button 的类型 */btnType?: ButtonType;children: React.ReactNode;href?: string;}export const Button: FC<BaseButtonProps> = (props) => {const { btnType, disabled, size, children, href } = props;const classes = classNames("btn", {[`btn-${btnType}`]: btnType,[`btn-${size}`]: size,disabled: btnType === "link" && disabled,});if (btnType === "link" && href) {return (<a className={classes} href={href}>{children}</a>);} else {return (<button className={classes} disabled={disabled} >{children}</button>);}};Button.defaultProps = {disabled: false,btnType: "default",};export default Button;
在第一版本代码可以看出,我们的Button中缺少原生组件所带的属性,我们来分别定义一下:
// buttontype NativeButtonProps = BaseButtonProps & ButtonHTMLAttributes<HTMLElement>;// atype AnchorButtonProps = BaseButtonProps & AnchorHTMLAttributes<HTMLElement>;
还要思考我们不会传递所有的属性,所以我们把属性设置为可选:
export type ButtonProps = Partial<NativeButtonProps & AnchorButtonProps>;
此外我们除了会传递BaseButtonProps上的参数,还会传递自己定义的className和原生标签上的其他属性。
我们来写第二版代码:
import React, { FC, ButtonHTMLAttributes, AnchorHTMLAttributes } from "react";import classNames from "classnames";export type ButtonSize = "lg" | "sm";export type ButtonType = "primary" | "default" | "danger" | "link";interface BaseButtonProps {className?: string;/**设置 Button 的禁用 */disabled?: boolean;/**设置 Button 的尺寸 */size?: ButtonSize;/**设置 Button 的类型 */btnType?: ButtonType;children: React.ReactNode;href?: string;}type NativeButtonProps = BaseButtonProps & ButtonHTMLAttributes<HTMLElement>;type AnchorButtonProps = BaseButtonProps & AnchorHTMLAttributes<HTMLElement>;export type ButtonProps = Partial<NativeButtonProps & AnchorButtonProps>;export const Button: FC<ButtonProps> = (props) => {const { btnType, className, disabled, size, children, href, ...restProps } =props;// btn, btn-lg, btn-primaryconst classes = classNames("btn", className, {[`btn-${btnType}`]: btnType,[`btn-${size}`]: size,disabled: btnType === "link" && disabled,});if (btnType === "link" && href) {return (<a className={classes} href={href} {...restProps}>{children}</a>);} else {return (<button className={classes} disabled={disabled} {...restProps}>{children}</button>);}};Button.defaultProps = {disabled: false,btnType: "default",};export default Button;
这样我们就完成了一个Button组件的全部逻辑。接下来我们解读一下样式编写;
5、样式编写
样式编写需要注意以下几点:
- 变量采用的是全局定义方式
- 重复代码采用mixin
代码如下:
.btn {position: relative;display: inline-block;font-weight: $btn-font-weight;line-height: $btn-line-height;color: $body-color;white-space: nowrap;text-align: center;vertical-align: middle;background-image: none;border: $btn-border-width solid transparent;@include button-size( $btn-padding-y, $btn-padding-x, $btn-font-size, $border-radius);box-shadow: $btn-box-shadow;cursor: pointer;transition: $btn-transition;&.disabled,&[disabled] {cursor: not-allowed;opacity: $btn-disabled-opacity;box-shadow: none;> * {pointer-events: none;}}}.btn-lg {@include button-size($btn-padding-y-lg, $btn-padding-x-lg, $btn-font-size-lg, $btn-border-radius-lg);}.btn-sm {@include button-size($btn-padding-y-sm, $btn-padding-x-sm, $btn-font-size-sm, $btn-border-radius-sm);}.btn-primary {@include button-style($primary, $primary, $white)}.btn-danger {@include button-style($danger, $danger, $white)}.btn-default {@include button-style($white, $gray-400, $body-color, $white, $primary, $primary)}.btn-link {font-weight: $font-weight-normal;color: $btn-link-color;text-decoration: $link-decoration;box-shadow: none;&:hover {color: $btn-link-hover-color;text-decoration: $link-hover-decoration;}&:focus,&.focus {text-decoration: $link-hover-decoration;box-shadow: none;}&:disabled,&.disabled {color: $btn-link-disabled-color;pointer-events: none;}}
