Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Antd源码浅析(一)Icon组件

Antd源码浅析(一)Icon组件

作者头像
河马嘴不大
发布于 2022-12-24 04:17:07
发布于 2022-12-24 04:17:07
2.1K00
代码可运行
举报
运行总次数:0
代码可运行

前言

最近在写B端的项目,用到了Ant Design,清爽而优雅。故想深入源码了解一二,但鉴于技术浅薄,不敢深究,故写浅析,不喜勿喷,对其中的组件做一些分析,主要目的有两个:

  • 学习Ant Design的工程设计思路
  • 思考怎样写出优秀的React组件

本文是基于Ant Design3.4.4的源码分析,读者需要具备基本的JavaScript、React知识,对于Antd(以下用Antd表示Ant Design),蚂蚁官网给出的定位是”一个服务于企业级产品的设计体系”,确实,我们的实际使用场景,大多是写一些后台页面,如CMS。当下的Antd比之React,就像Bootstrap比之jQuery,同样Vue也不乏有Element UI之类的搭档。

Antd源码是基于Typescript(系出微软,是 JavaScript 的一个类型超集,包含它自己的编译器,是一种类型化语言),如果有阅读过Vue源码的的同学肯定也会发现,Vue中使用了Flow来做同样的事,即静态类型检查。JavaScript是弱类型语言,很多大型库都加入了Flow或者Typescript,严谨为之。

目录结构

打开Antd源码目录,结构还是比较简洁:

平时所用到的组件全部位于 components 文件夹下,首先我们分析一个简单的组件Icon,打开 components/icon ,目录结构如下:

这里不得不说Antd的文档是很友好的,目录内的以 .md 结尾的文件给出了中英文的使用说明,也就是我们在在其官网看到的说明文档。

代码

Icon的核心代码位于 index.tsx 内,这里说明一下,对于不熟悉Typescript的同学来说这个文件类型可能有些陌生,Typescript主要是丰富了JavaScript的内容和加入了静态类型检查,一般的Typescript文件是以 .ts 结尾,但相对于React的jsx文件,Typescript产生了 .tsx 的文件,其实就是Typescript的jsx写法,实际生产环境中,最终都要编译成 .js 文件。

以下是Icon组件中 index.tsx 的全部源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import * as React from 'react';
import classNames from 'classnames';
import omit from 'omit.js';

export interface IconProps {
  type: string;
  className?: string;
  title?: string;
  onClick?: React.MouseEventHandler<any>;
  spin?: boolean;
  style?: React.CSSProperties;
}

const Icon = (props: IconProps) => {
  const { type, className = '', spin } = props;
  const classString = classNames({
    anticon: true,
    'anticon-spin': !!spin || type === 'loading',
    [`anticon-${type}`]: true,
  }, className);
  return <i {...omit(props, ['type', 'spin'])} className={classString} />;
};

export default Icon;

我们看看官网使用示例和API描述:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<Icon type="question" style={{ fontSize: 16, color: '#08c' }} />

参数

说明

类型

默认值

spin

是否有旋转动画

boolean

false

style

设置图标的样式,例如 fontSize 和 color

object

-

type

图标类型

string

-

首先导入的是3个依赖

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import * as React from 'react';
import classNames from 'classnames';
import omit from 'omit.js';

大家对React比较熟悉,对于classnamesomit.js,这里做些说明。

classNames基本使用方法

classnames主要是为组件提供动态css功能,方便向React之类的应用提供状态编程

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var classNames = require('classnames');
classNames('foo', 'bar'); // => 'foo bar'

classNames('foo', { bar: true }); // => 'foo bar'
classNames({ 'foo-bar': true }); // => 'foo-bar'
classNames({ 'foo-bar': false }); // => ''
classNames({ foo: true }, { bar: true }); // => 'foo bar'
classNames({ foo: true, bar: true }); // => 'foo bar'

// 不同的参数类型
classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'

// 忽略错误的数据类型
classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'

// 数组参数
var arr = ['b', { c: true, d: false }];
classNames('a', arr); // => 'a b c'

classnames能够很简便的处理css的class开关,类似于在jsx中

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{ true? 'class-a': 'class-b' }

但是要优雅和方便很多,结合ES2015中的字符串变量,就可以玩的更开心

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let buttonType = 'primary';
classNames({ [`btn-${buttonType}`]: true });

当然怎么能少了直接使用React

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var classNames = require('classnames');

var Button = React.createClass({
  // ...
  render () {
    var btnClass = classNames({
      btn: true,
      'btn-pressed': this.state.isPressed,
      'btn-over': !this.state.isPressed && this.state.isHovered
    });
    return <button className={btnClass}>{this.props.label}</button>;
  }
});
omit.js基本使用方法

omit.js,作用就是过滤掉对象中不需要的属性,避免把不必要的属性传递下去

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var omit = require('omit.js');
omit({ name: 'Benjy', age: 18 }, [ 'name' ]); // => { age: 18 }

这个库的源码很简单,直接贴出:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function omit(obj, fields) {
  const shallowCopy = {
    ...obj,
  };
  for (let i = 0; i < fields.length; i++) {
    const key = fields[i];
    delete shallowCopy[key];
  }
  return shallowCopy;
}

export default omit;
属性校验

接下来我们看看 IconPropsIconProps 是Icon组件的参数验证器,作用和React中的 PropTypes 相同,确保你接收到的数据是有效的,能够在识别些某些类型问题,所以React官方也建议,对于更大的代码库使用Flow或者TypeScript来替代 PropTypes ,Antd的开发使用了TypeScript。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
export interface IconProps {
  type: string;    // 图标类型必须为string
  className?: string;   // className类型必须为string
  title?: string;   // title类型必须为string
  onClick?: React.MouseEventHandler<any>;    // onClick类型必须为React.MouseEventHandler
  spin?: boolean;    // 是否有旋转动画类型必须为boolean
  style?: React.CSSProperties;  // style类型必须为React.CSSProperties
}

在这里 ? 代表参数可选,对于 React.MouseEventHandler<any>React.CSSProperties 是TypeScript为React定义的数据类型, <> 为泛型标识,我们不妨以 React.MouseEventHandler<any> 为例子深入看一下TypeScript实现的事件类型定义,如果不理解,可以简单理解为一种数据类型。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 第一层
React.MouseEventHandler<any>  
  
// 第二层
type MouseEventHandler<T> = EventHandler<MouseEvent<T>>;    

// 第三层
interface MouseEvent<T> extends SyntheticEvent<T> {    
    altKey: boolean;
    button: number;
    buttons: number;
    clientX: number;
    clientY: number;
    ctrlKey: boolean;
    /**
     * See [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#keys-modifier). for a list of valid (case-sensitive) arguments to this method.
     */
    getModifierState(key: string): boolean;
    metaKey: boolean;
    nativeEvent: NativeMouseEvent;
    pageX: number;
    pageY: number;
    relatedTarget: EventTarget;
    screenX: number;
    screenY: number;
    shiftKey: boolean;
}    


// 第四层
// Event System
// ----------------------------------------------------------------------

interface SyntheticEvent<T> {
    bubbles: boolean;
    /**
     * A reference to the element on which the event listener is registered.
     */
    currentTarget: EventTarget & T;
    cancelable: boolean;
    defaultPrevented: boolean;
    eventPhase: number;
    isTrusted: boolean;
    nativeEvent: Event;
    preventDefault(): void;
    isDefaultPrevented(): boolean;
    stopPropagation(): void;
    isPropagationStopped(): boolean;
    persist(): void;
    // If you thought this should be `EventTarget & T`, see https://github.com/DefinitelyTyped/DefinitelyTyped/pull/12239
    /**
     * A reference to the element from which the event was originally dispatched.
     * This might be a child element to the element on which the event listener is registered.
     *
     * @see currentTarget
     */
    target: EventTarget;
    timeStamp: number;
    type: string;
}

以上是React类型定义的源码,小伙伴们是不是能够理解一些了,如果我们用React本身实现Icon的验证,有如下写法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import PropTypes from 'prop-types';

Icon.propTypes = {
  type: PropTypes.string;
  className: PropTypes.string;
  title: PropTypes.string;
  onClick: PropTypes.func;
  spin: PropTypes.bool;
  style: PropTypes.object;
};
主体代码
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const Icon = (props: IconProps) => {
  const { type, className = '', spin } = props;
  const classString = classNames({
    anticon: true,
    'anticon-spin': !!spin || type === 'loading',
    [`anticon-${type}`]: true,
  }, className);
  return <i {...omit(props, ['type', 'spin'])} className={classString} />;
};

可以看到Antd使用 <i> 标签来实现Icon组件,首先通过 IconProps 校验参数,然后组合 className ,默认添加 anticon ,判断 spin 属性,选择是否添加 anticon-spin ,接着添加 anticon-${type}属性,生成 className ,通过 omit 过滤掉 type , spin 属性,因为这俩属性对于 <i> 标签是没有意义的,为了理解我们举个实际使用例子。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<Icon type="question" style={{ fontSize: 16 }} />

生成的HTML中的代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<i class="anticon anticon-question" style="font-size: 16px;"></i>

总结

到这里对于Icon组件,我们就能直观的看到其实现原理了,可能部分读者对于TypeScript这块有些疑虑,可以简单理解为数据类型校验,这里我们能够学习到:

  • Antd组件实现的基本结构和思路
  • 组件对于参数的校验的方式
  • 优雅的处理 classNames
  • 省略一些不必要的参数
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-05-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Antd源码浅析(二)InputNumber组件 一
上篇我们讲了Icon组件,Icon组件是Antd源码库中实现比较简单的组件,适合大家入门,这篇文章主要和大家一起分析一下数字输入框组件,即InputNumber,难度适中,但蕴含的Antd里较为经典的开发场景,适合大家比较深入的了解Antd背后的思想。
河马嘴不大
2022/12/24
2.2K0
Antd源码浅析(二)InputNumber组件 一
ant design源码分析 1 研究方法
ant design 是一套设计语言。 这里为了学习react,我主要学习用 React实现 的各个组件。这个是由官方维护的,代码质量高些。还有 基于vue 实现的。 源码在 github 上。 阅读readme打开 开发者说明 ├── components # react source code and demos ├── docs # documentation in markdown ├── scripts # ├── site # website layout and code └── package.json
mafeifan
2018/09/10
2.3K0
实战为王,从零封装 Icon 组件
在实践应用中,图标的使用无处不在。小到编辑器的功能按钮,大到 chrome 浏览器的任务栏,都有大量的图标需要处理。每个稍微大一点点的项目都必然需要一个图标组件。
用户6901603
2022/05/24
1.4K0
实战为王,从零封装 Icon 组件
Antd源码浅析(三)InputNumber组件 二
上篇我们讲了InputNumber组件的主要结构组成,本篇我们来分析,InputNumber组件的核心实现rc-input-number的源码,即<RcInputNumber/>组件。虽然我们是浅析Antd组件,但本着专注、严谨的科学态度,这里河马君会为大家从Antd库入手,讲解背后的依赖库,深入到最底层,主要一个是为了理解代码,另一个是看看大家都用哪些开源库,加快开发进度。
河马嘴不大
2022/12/24
1.3K0
Antd源码浅析(三)InputNumber组件 二
🔖TypeScript 备忘录:如何在 React 中完美运用?
一直以来,ssh 身边都有很多小伙伴对 TS 如何在 React 中运用有很多困惑,他们开始慢慢讨厌 TS,觉得各种莫名其妙的问题降低了开发的效率。
ssh_晨曦时梦见兮
2022/03/09
3K0
🔖TypeScript 备忘录:如何在 React 中完美运用?
【React】【案例】:TimeLine 时间轴
目录 1. 组件基础 2. 需求分析 3. 关键技术 4. 代码实现 5. 形态展示 1. 组件基础 可视化地呈现时间流信息。 2. 需求分析 3. 关键技术 为什么不直接用 antd、elementui、iview 等开源组件? antd 很优秀,但是.... antd 不支持 label、content 按指定比例分布; antd 在dot定制时,难以控制UI界面呈现; elementui 不能将 label 放在左边; .... 但是以 antd 为基础改造,会快很多; 主体采用什么html结构实
WEBJ2EE
2020/04/21
8.4K0
【React】【案例】:TimeLine 时间轴
React造轮系列:Layout 组件思路
参考 And Design ,Layout 组件分别分为 Layout, Header, Aside, Content,Footer 五个组件。基本使用结构如下:
前端小智@大迁世界
2019/06/15
3K0
React 造轮子系列:Icon 组件思路
本轮子是通过 React + TypeScript + Webpack 搭建的,至于环境的搭建这边就不在细说了,自己动手谷歌吧。当然可以参考我的源码。
Javanx
2019/10/15
2.2K0
React 造轮子系列:Icon 组件思路
【React】【案例】:简易轮播组件
目录 1. 组件展示 2. 关键技术 3. 关键实现 4. 组件接口 1. 组件展示 组件特性: 滑动箭头,只有当待滑动内容无法完整显示时才出现。 滑动过程使用动画体现。 滑动到左边界时,左滑动箭头给出不可滑动标识。 滑动到右边界时,右滑动箭头给出不可滑动标识。 浏览器缩放时,也能满足上述条件。 2. 关键技术 如何实现竖直居中? absolute + top:50% + transform(-50%, -50%) 如何避免用户点击滑动箭头时,意外选中文本? css3 -> user-select:non
WEBJ2EE
2020/04/21
1.3K0
【React】【案例】:简易轮播组件
前端反卷计划-组件库-05-Menu组件开发
在渲染childrend的时候,使用React.cloneElement将index克隆到child上
程序员库里
2023/11/27
2630
天天用 antd 的 Form 组件?自己手写一个吧
用 Form.Item 包裹 Input、Checkbox 等表单项,可以定义 rules,也就是每个表单项的校验规则。
神说要有光zxg
2024/04/10
4130
天天用 antd 的 Form 组件?自己手写一个吧
React + TypeScript 实践
需要添加额外的配置:"allowSyntheticDefaultImports": true
公众号@魔术师卡颂
2021/05/08
6.7K0
前端反卷计划-组件库-06-Icon组件开发
首先在src/styles/_variables.scss增加theme-color变量
程序员库里
2023/11/27
3370
《精通react/vue组件设计》之实现一个健壮的警告提示(Alert)组件
本文是笔者写组件设计的第七篇文章, 今天带大家实现一个自带主题且可关闭的Alert组件, 该组件在诸如Antd或者elementUI等第三方组件库中都会出现,主要用来提供系统的用户反馈.
徐小夕
2020/02/19
1.1K0
《精通react/vue组件设计》之5分钟实现一个Tag(标签)组件和Empty(空状态)组件
本文是笔者写组件设计的第五篇文章,之所以会写组件设计相关的文章,是因为作为一名前端优秀的前端工程师,面对各种繁琐而重复的工作,我们不应该按部就班的去"辛勤劳动",而是要根据已有前端的开发经验,总结出一套自己的高效开发的方法.作为数据驱动的领导者react/vue等MVVM框架的出现,帮我们减少了工作中大量的冗余代码, 一切皆组件的思想深得人心.所以, 为了让工程师们有更多的时间去考虑业务和产品迭代,我们不得不掌握高质量组件设计的思路和方法.所以笔者将花时间去总结各种业务场景下的组件的设计思路和方法,并用原生框架的语法去实现各种常用组件的开发,希望等让前端新手或者有一定工作经验的朋友能有所收获.
徐小夕
2020/02/19
1.5K0
React-hooks+TypeScript最佳实战
如果新的 state 需要通过使用先前的 state 计算得出,那么可以将回调函数当做参数传递给 setState。该回调函数将接收先前的 state,并返回一个更新后的值。
xiaofeng123aa
2022/10/17
6.3K0
React 造轮子系列:Icon 组件思路
本轮子是通过 React + TypeScript + Webpack 搭建的,至于环境的搭建这边就不在细说了,自己动手谷歌吧。当然可以参考我的源码。
前端小智@大迁世界
2019/06/15
4.8K1
基于 React 实现一个 Transition 过渡动画组件
过渡动画使 UI 更富有表现力并且易于使用。如何使用 React 快速的实现一个 Transition 过渡动画组件?
用户6167509
2020/02/28
6.1K0
让你 React 组件水平暴增的 5 个技巧
最近看了一些 Ant Design 的组件源码,学到一些很实用的技巧,这篇文章来分享一下。
神说要有光zxg
2023/08/29
6120
让你 React 组件水平暴增的 5 个技巧
「TS实践」自己动手丰衣足食的TS项目开发
之前看antd的源码,已经使用TypeScript重写了。对于像我这种喜欢通过实际项目学习技术的人,非常的友好。
叶一一
2022/10/24
1.8K0
「TS实践」自己动手丰衣足食的TS项目开发
相关推荐
Antd源码浅析(二)InputNumber组件 一
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验