Antd源码浅析(二)InputNumber组件 一

前言

上篇我们讲了Icon组件,Icon组件是Antd源码库中实现比较简单的组件,适合大家入门,这篇文章主要和大家一起分析一下数字输入框组件,即InputNumber,难度适中,但蕴含的Antd里较为经典的开发场景,适合大家比较深入的了解Antd背后的思想。

这篇我们学习的目的主要有:

  • 学习Antd的如何基于现有的组件封装
  • 封装背后的技术目的和效果如何

目录结构

首先我们依旧看看位于components 文件夹下的input-number 目录结构:

alt text

InputNumber组件的的效果图如下:

alt text

代码

InputNumber的核心代码位于 index.tsx 内,代码不多,我们直接贴过来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import * as React from 'react';
import classNames from 'classnames';
import RcInputNumber from 'rc-input-number';

export interface InputNumberProps {
prefixCls?: string;
min?: number;
max?: number;
value?: number;
step?: number | string;
defaultValue?: number;
tabIndex?: number;
onKeyDown?: React.FormEventHandler<any>;
onChange?: (value: number | string | undefined) => void;
disabled?: boolean;
size?: 'large' | 'small' | 'default';
formatter?: (value: number | string | undefined) => string;
parser?: (displayValue: string | undefined) => number;
placeholder?: string;
style?: React.CSSProperties;
className?: string;
name?: string;
id?: string;
precision?: number;
}

export default class InputNumber extends React.Component<InputNumberProps, any> {
static defaultProps = {
prefixCls: 'ant-input-number',
step: 1,
};

private inputNumberRef: any;

render() {
const { className, size, ...others } = this.props;
const inputNumberClass = classNames({
[`${this.props.prefixCls}-lg`]: size === 'large',
[`${this.props.prefixCls}-sm`]: size === 'small',
}, className);

return <RcInputNumber ref={(c: any) => this.inputNumberRef = c} className={inputNumberClass} {...others} />;
}

focus() {
this.inputNumberRef.focus();
}

blur() {
this.inputNumberRef.blur();
}
}

主要结构非常清晰,分成三个部分,头部的文件引入,参数校验,主体类声明。

文件的引入中,react大家非常熟悉,classnames 在上篇文章,河马君为大家介绍过使用方法和实现,对于rc-input-number可能部分读者比较陌生,我们来介绍一下。

Antd的许多组件都是基于rc-xxx组件分装,比如常见的Table组件是基于rc-table,Form组件基于rc-form,rc-xxx来源于react-component组件库,里面有很多常用的组件,大家也可以在项目中直接使用,也可以经过自己二次封装后使用。我们稍后仔细分析一下rc-input-number,先来看看参数校验和主体结构。

参数校验

对于参数校验,当然需要对照InputNumber的文档看了,官方的使用说明如下:

属性如下:

成员 说明 类型 默认值
autoFocus 自动获取焦点 boolean false
defaultValue 初始值 number
disabled 禁用 boolean false
formatter 指定输入框展示值的格式 function(value: number \ string): string
max 最大值 number Infinity
min 最小值 number -Infinity
parser 指定从 formatter 里转换回数字的方式,和 formatter 搭配使用 function( string): number -
precision 数值精度 number -
size 输入框大小 string
step 每次改变步数,可以为小数 number\ string
value 当前值 number
onChange 变化回调 Function(value: number \ string)

那我们再来对照代码里的参数校验,文档中有的就不再赘述,缺少的通过注释给出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
export interface InputNumberProps {
prefixCls?: string; // Antd预留给自己的预设class,这里在defaultProps中默认设置为'ant-input-number'
min?: number;
max?: number;
value?: number;
step?: number | string;
defaultValue?: number;
tabIndex?: number; //tab 键控制次序,就是快捷切换
onKeyDown?: React.FormEventHandler<any>; // 用户按下键盘按键时的回调函数
onChange?: (value: number | string | undefined) => void;
disabled?: boolean;
size?: 'large' | 'small' | 'default';
formatter?: (value: number | string | undefined) => string;
parser?: (displayValue: string | undefined) => number;
placeholder?: string; // placeholder提示
style?: React.CSSProperties; // 用户自定义style
className?: string; // 用户自定义class
name?: string; // 用户自定义name属性,毕竟底层是input标签
id?: string; // 用户自定义id
precision?: number;
}

对于其中TypeScript形式类型校验,可以参考上篇文章,这里Antd文档给出了其自定义的参数列表,省略了默认的普通参数,所以通过对于源码的学习,能够清晰的知道在官方文档之外,哪些参数是可用的。

主体函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
export default class InputNumber extends React.Component<InputNumberProps, any> {
// 默认参数设置
static defaultProps = {
prefixCls: 'ant-input-number',
step: 1,
};

// 通过ref属性获取实例
private inputNumberRef: any;

render() {
const { className, size, ...others } = this.props;

// className的判断和合成
const inputNumberClass = classNames({
[`${this.props.prefixCls}-lg`]: size === 'large',
[`${this.props.prefixCls}-sm`]: size === 'small',
}, className);

// 核心RcInputNumber组件,我们稍后讲解一下
return <RcInputNumber ref={(c: any) => this.inputNumberRef = c} className={inputNumberClass} {...others} />;
}

// 通过实例绑定的focus事件和blur事件
focus() {
this.inputNumberRef.focus();
}

blur() {
this.inputNumberRef.blur();
}
}

这是官方给出的方法文档:

名称 描述
blur() 移除焦点
focus() 获取焦点

这里河马君多说一下,对于这两个函数的实现,Antd使用了ref属性,实现对组件的引用

1
ref={(c: any) => this.inputNumberRef = c}

这是通过ref回调的方式,在组件render完获取实例,优于React提供的旧版的this.refs.inputNumberRef字符串形式,但在最新版React16.2的文档中,官方建议使用React.createRef(),这里用普通的写法可以写为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  constructor(props) {
super(props);
this.inputNumberRef = React.createRef();
}
...
render() {
...
return <RcInputNumber ref={this.inputNumberRef} className={inputNumberClass} {...others} />;
}

focus() {
this.inputNumberRef.focus();
}

blur() {
this.inputNumberRef.blur();
}
}

到这里,对于InputNumber组件的源码主体结构介绍完了,我们下一篇文章,分析一下核心的rc-input-number源码,即<RcInputNumber/>组件的实现。