当初阿里一面的时候,被问到两个问题哑口无言,一个是你对开源社区的贡献,一个是你看没看过antd的源码。
所以我挑了一个比较简单的组件,switch看看。

一开始跑了个demo,然后再node_modules里看antd的那个包

│ index.d.ts
│ index.js

└─style
css.js
index.css
index.d.ts
index.js
index.less

就算是我也知道阿里的程序员不可能直接写js文件,所以就机智的看起了index.d.ts

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
import * as React from 'react';
import * as PropTypes from 'prop-types';
export interface SwitchProps {
prefixCls?: string;
size?: 'small' | 'default';
className?: string;
checked?: boolean;
defaultChecked?: boolean;
onChange?: (checked: boolean) => any;
checkedChildren?: React.ReactNode;
unCheckedChildren?: React.ReactNode;
disabled?: boolean;
loading?: boolean;
}
export default class Switch extends React.Component<SwitchProps, {}> {
static defaultProps: {
prefixCls: string;
};
static propTypes: {
prefixCls: PropTypes.Requireable<string>;
size: PropTypes.Requireable<string>;
className: PropTypes.Requireable<string>;
};
private rcSwitch;
focus(): void;
blur(): void;
saveSwitch: (node: any) => void;
render(): JSX.Element;
}

额,感觉只是声明和类型限制,于是看了看index.js,虽然不算太长,但一看就是编译之后的东西。
然后我看到了 这个
当然应该去分析这个了。于是我去下载了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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import * as React from 'react';
import * as PropTypes from 'prop-types';
import RcSwitch from 'rc-switch';
import classNames from 'classnames';
import omit from 'omit.js';
import Wave from '../_util/wave';

export interface SwitchProps {
prefixCls?: string;
size?: 'small' | 'default';
className?: string;
checked?: boolean;
defaultChecked?: boolean;
onChange?: (checked: boolean) => any;
checkedChildren?: React.ReactNode;
unCheckedChildren?: React.ReactNode;
disabled?: boolean;
loading?: boolean;
}

export default class Switch extends React.Component<SwitchProps, {}> {
static defaultProps = {
prefixCls: 'ant-switch',
};

static propTypes = {
prefixCls: PropTypes.string,
// HACK: https://github.com/ant-design/ant-design/issues/5368
// size=default and size=large are the same
size: PropTypes.oneOf(['small', 'default', 'large']),
className: PropTypes.string,
};

private rcSwitch: typeof RcSwitch;

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

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

saveSwitch = (node: typeof RcSwitch) => {
this.rcSwitch = node;
}

render() {
const { prefixCls, size, loading, className = '' } = this.props;
const classes = classNames(className, {
[`${prefixCls}-small`]: size === 'small',
[`${prefixCls}-loading`]: loading,
});
return (
<Wave insertExtraNode>
<RcSwitch
{...omit(this.props, ['loading'])}
className={classes}
ref={this.saveSwitch}
/>
</Wave>
);
}
}

其实可以看到就是对”rc-switch”的一个简单封装,外层的Wave是antd点击时候的波纹特效。
然后我们去看rc-switch的源码(这个rc是react-component)的简称,这玩意也是阿里他们做的。

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import React, { Component } from 'react';
import PropTypes from 'prop-types';
const classNames = require('classnames');

function noop() {
}

class Switch extends Component {
constructor(props) {
super(props);

let checked = false;
if ('checked' in props) {
checked = !!props.checked;
} else {
checked = !!props.defaultChecked;
}
this.state = { checked };
}

componentDidMount() {
const { autoFocus, disabled } = this.props;
if (autoFocus && !disabled) {
this.focus();
}
}

componentWillReceiveProps(nextProps) {
if ('checked' in nextProps) {
this.setState({
checked: !!nextProps.checked,
});
}
}

setChecked(checked) {
if (this.props.disabled) {
return;
}
if (!('checked' in this.props)) {
this.setState({
checked,
});
}
this.props.onChange(checked);
}

toggle = () => {
const { onClick } = this.props;
const checked = !this.state.checked;
this.setChecked(checked);
onClick(checked);
}

handleKeyDown = (e) => {
if (e.keyCode === 37) { // Left
this.setChecked(false);
} else if (e.keyCode === 39) { // Right
this.setChecked(true);
} else if (e.keyCode === 32 || e.keyCode === 13) { // Space, Enter
this.toggle();
}
}

// Handle auto focus when click switch in Chrome
handleMouseUp = (e) => {
if (this.node) {
this.node.blur();
}
if (this.props.onMouseUp) {
this.props.onMouseUp(e);
}
}

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

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

saveNode = (node) => {
this.node = node;
}

render() {
const { className, prefixCls, disabled, loadingIcon,
checkedChildren, tabIndex, unCheckedChildren, ...restProps } = this.props;
const checked = this.state.checked;
const switchTabIndex = disabled ? -1 : (tabIndex || 0);
const switchClassName = classNames({
[className]: !!className,
[prefixCls]: true,
[`${prefixCls}-checked`]: checked,
[`${prefixCls}-disabled`]: disabled,
});
return (
<span
{...restProps}
className={switchClassName}
tabIndex={switchTabIndex}
ref={this.saveNode}
onKeyDown={this.handleKeyDown}
onClick={this.toggle}
onMouseUp={this.handleMouseUp}
>
{loadingIcon}
<span className={`${prefixCls}-inner`}>
{checked ? checkedChildren : unCheckedChildren}
</span>
</span>
);
}
}

Switch.propTypes = {
className: PropTypes.string,
prefixCls: PropTypes.string,
disabled: PropTypes.bool,
checkedChildren: PropTypes.any,
unCheckedChildren: PropTypes.any,
onChange: PropTypes.func,
onMouseUp: PropTypes.func,
onClick: PropTypes.func,
tabIndex: PropTypes.number,
checked: PropTypes.bool,
defaultChecked: PropTypes.bool,
autoFocus: PropTypes.bool,
loadingIcon: PropTypes.node,
};

Switch.defaultProps = {
prefixCls: 'rc-switch',
checkedChildren: null,
unCheckedChildren: null,
className: '',
defaultChecked: false,
onChange: noop,
onClick: noop,
};

export default Switch;

这个文件是用js写的,antd后期用ts重写过一次,数据类型限制还用的propTypes,默认props用defaultProps.
值得一提的是tabIndex,提供了使用tap键在表单中切换元素的工能,还有onKeyDown提供了键盘操作。!!强制转义bool变量
在less文件中cubic-bezier定义了css3动画使用贝塞尔曲线。