技术标签: ant design pro react dva
以登录模块为例,对ant design pro的组件封装进行相关分析。登录模块包含基础组件的封装、组件按模块划分、同类组件通过配置文件生成、跨层级组件直接数据通信等,相对来说还是具有一定的代表性。
首先,全局了解一下登录模块的总体流程。如下图所示。该流程图主要分两部分:1、页面布局;2、组件封装。黄色实线表示页面中组件的引用。下边会对基础组件分析,以及多层级组件直接的数据处理。
由上图可知,所有的元素都是作为Login组件的children存在,依次包含Tab、UserName、Password、Mobile、Captcha、Submit。为了便于组件间关系更加明确,所有这些子组件都封装在Login组件中。
const {Tab, UserName, Password, Mobile, Captcha, Submit} = Login;
由以上介绍可知,Login作为其他组件的父组件。涉及到跨层数据通信,子组件可以通过context属性对Login的数据进行操作。
为便于对后续跨组件间数据通信有更好的了解,首先分析下跨层组件的数据模型。由React的API可知,如果需要用到context实现跨层级之间通信,首先需要在父组件中声明childContextTypes属性,并通过getChildContext返回对象,同时在使用context的子组件中通过contextTypes属性声明引用。
在父组件Login组件中,有如下context相关的代码:
......
static childContextTypes = {
tabUtil: PropTypes.object,
form: PropTypes.object,
updateActive: PropTypes.func,
};
......
getChildContext() {
return {
tabUtil: {
addTab: id => {
this.setState({
tabs: [...this.state.tabs, id],
});
},
removeTab: id => {
this.setState({
tabs: this.state.tabs.filter(currentId => currentId !== id),
});
},
},
form: this.props.form,
updateActive: activeItem => {
// type -- account、 mobile
//active 用来将account、mobile 的表单字段分类,便于提交校验。
const {
type, active } = this.state;
if (active[type]) {
active[type].push(activeItem);
} else {
active[type] = [activeItem];
}
this.setState({
active,
});
},
};
}
childContextTypes对应的以上三个属性在组件中会被调用,子组件通过调用这些方法和属性,来改变父组件的状态。
LoginTab组件的处理比较简单,主要是以下三个部分:
LoginTab组件定义了两个静态属性:__ANT_PRO_LOGIN_TAB、contextTypes。
其中__ANT_PRO_LOGIN_TAB是为了给当前组件设置一个flag位。在Login组件中用于判断子组件是用LoginTab渲染还是其他处理方式;
contextTypes是配合context属性,跨层传递组件间的数据,声明引用父组件(声明childContextTypes的组件)中的tabUtil属性。
static __ANT_PRO_LOGIN_TAB = true;
static contextTypes = {
tabUtil: PropTypes.object,
};
LoginTab组件调用了生命周期函数componentWillMount,其目的是调用父组件中context返回的addTab方法。传递的参数,是生成的唯一字符串(generateId方法返回)。
componentWillMount() {
if (this.context.tabUtil) {
this.context.tabUtil.addTab(this.uniqueId);
}
}
render方法返回TabPane组件(引自 ant design Tab组件)。其中将父组件传递过来的prop全部继承,包含子组件。
render() {
return <TabPane {
...this.props} />;
}
以下是generator部分代码。详细代码请参考原文件,此处只对关键代码进行分析。
function generator({
defaultProps, defaultRules, type }) {
return WrappedComponent => {
return class BasicComponent extends Component {
static contextTypes = {
form: PropTypes.object,
updateActive: PropTypes.func,
};
constructor(props) {
......
}
componentDidMount() {
if (this.context.updateActive) {
this.context.updateActive(this.props.name);
}
}
componentWillUnmount() {
clearInterval(this.interval);
}
onGetCaptcha = () => {
......
};
render() {
......
const {
getFieldDecorator } = this.context.form;
......
//组件参数
const {
onChange, defaultValue, rules, name, ...restProps } = this.props;
......
return (
<FormItem>
{
getFieldDecorator(name, options)(
<WrappedComponent {
...defaultProps} {
...otherProps} />
)}
</FormItem>
);
}
};
};
}
由上述代码可知,generator 是一个高阶函数,返回的是BasicComponent类,BasicComponent本质上一个组件:组件类型取决于参数WrappedComponent,组件属性取决于参数defaultProps、defaultRules(默认参数),以及组件引用时传递的props参数(this.props)(动态参数)。
其中,contextTypes中申明的form属性,来自于父组件Login中的【Form.create()(Login)】form属性,因此可以使用getFieldDecorator对表单元素绑定。(这也说明,generator是表单元素生成器)。
render方法中,当type==='Captcha’时,有做特殊处理。该部分逻辑对应的业务是通过手机号获取验证码。对应的
onGetCaptcha方法设定了获取验证码的倒计时逻辑。componentWillUnmount中对定时器清楚。
componentDidMount方法,将props属性中的name属性传递给context中的updateActive方法。在第2部分已经阐述过,updateActive方法根据type(accout,mobile)来区分各自的表单字段,便于数据校验和数据提交。
map.js 本质上是一个配置文件,其内容为登录模块中表单元素的配置,通过对配置文件数据的处理,得到对应的表单元素组件(详见4.3)。以下边代码为例,该配置文件的key(UserName)即为组件名称,即生成UserName组件,component表示UserName组件最终返回的组件,即UserName组件本质上是Input组件,其中props中的参数与ant design 组件Input的属性保持一致。rules属性,包含当前组件需要满足的约束、规则,该表单元素最终会通过form属性中提供的方法进行数据绑定,因此rules属性与ant design 组件Form表单的属性保持一致。
const map = {
UserName: {
component: Input,
props: {
size: 'large',
prefix: <Icon type="user" className={
styles.prefixIcon} />,
placeholder: 'admin',
},
rules: [
{
required: true,
message: 'Please enter username!',
},
],
}
}
通过循环遍历配置文件map中的key,通过4.1 generator生成器分析可知,调用generator方法后,生成的LoginItem[item] 组件,实际上是map[item].component元素(配置文件中component属性),同时传入的默认参数、默认校验分别为map[item].props、map[item].rules(配置文件中props,rules属性),其中type是为了对特殊元素进行处理。
const LoginItem = {
};
Object.keys(map).forEach(item => {
LoginItem[item] = generator({
defaultProps: map[item].props,
defaultRules: map[item].rules,
type: item,
})(map[item].component);
});
表单元素通过统一的配置文件map.js形成集合,循环遍历配置文件map.js,生成对应的表单元素组件。其中表单元素组件的最终的展现形式取决于配置文件中的component属性;结合表单元素引用时的动态属性(onChange、defaultValue、name等)和配置文件中的静态属性(props),确定该组件最终的属性;结合表单元素引用时的rules属性和配置文件的rules属性,确定最终的校验规则。其中props、rules都符合ant design组件库中相关组件的配置属性。(文档流)
所有的表单组件作为Login的子组件,通过调用context属性对数据进行关联。调用form属性中的getFieldDecorator对表单元素进行数据绑定。调用updateActive对表单元素根据tab类型进行分类。(数据流)
Login组件作为其他子组件的父组件,通过childContextTypes、getChildContext完成与子组件的数据交互(当然子组件中需要声明),关于数据流部分,已在上文中各个模块处都有详解,此处不再赘述。
处理Tab切换,以及表单提交等事件。其中子组件生成时已经根据tab组件的类型(accout、mobile)对表单元素进行划分,因此在校验时,只需要对当前tab组件下的表单进行校验和提交。因此有如下代码:
handleSubmit = e => {
e.preventDefault();
const {
active, type } = this.state;
const activeFileds = active[type];
this.props.form.validateFields(activeFileds, {
force: true }, (err, values) => {
this.props.onSubmit(err, values);
});
};
其中type为当前tab组件的类型。则activeFileds为当前tab组件下的表单元素,因此校验时,只需要调用this.props.form.validateFields方法对activeFileds校验(https://ant.design/components/form-cn/) ,校验通过则调用接口向后台业务传递数据(routes/User/Login中定义)。
其中,render方法中,下边这段代码的主要作用是为了判断Login组件的子元素是否是Tab组件。其中Tab组件引用来自LoginTab。LoginTab组件中定义了静态属性__ANT_PRO_LOGIN_TAB,该字段目的就是为了区分是否是Tab组件(最终形式为TabPane)。其中Tab组件最终被ant desgin的Tabs组件包裹,最终生成 <Tabs><TabPane/><TabPane/></Tabs>
形式。非Tab组件,就按照原有模式渲染(<div>,<Submit>
等)。
React.Children.forEach(children, item => {
if (!item) {
return;
}
if (item.type.__ANT_PRO_LOGIN_TAB) {
TabChildren.push(item);
} else {
otherChildren.push(item);
}
});
其中在Login组件中由如下代码,其目的是,将LoginItem中的表单元素集合统一配置在Login组件,便于组件间管理。其中LoginSubmit组件较为简单,就是对ant design中Button的封装,此处不再赘述。
Login.Tab = LoginTab;
Login.Submit = LoginSubmit;
Object.keys(LoginItem).forEach(item => {
Login[item] = LoginItem[item];
});
由以上分析可知,Login组件间的数据流如下图所示。
上一篇:ant design pro 代码学习(六) ----- 知识点总结2
开篇:ant design pro 代码学习(一) ----- 路由分析
文章浏览阅读712次,点赞19次,收藏30次。为了便于更好地理解,我们将整个方案分为 2 个部分进行介绍。一期方案:主要介绍在 Java 大对象通过 LargeObjectSpace 的内存申请和释放过程中,如何在内存申请和释放过程对其进行改造,以脱离虚拟机对这些对象的内存管理,最后实现 LargeObjectSpace 占用的内存完全脱离虚拟机内存统计。二期方案:针对一期方案需要在应用运行过程中提前开启,但是线上 99%以上运行过程中可能不会发生 OOM,因此一期方案对系统的侵入有点高。
文章浏览阅读48次。atitit.解决net.sf.json.JSONExceptionThereisacycleinthehierarchy1.环境:使用hibernate4跟个,,要不个哪的对象系列化成个json的时候儿有这个问题了... 12.原因::hb默认的lazy方式造成的当有关联对象的时候儿... 13.#---解决::lazy=fal...
文章浏览阅读3.4k次,点赞3次,收藏4次。高速主频240MHZ,对比STM32F1的72MHZ和STM32F4的168MHZ,速度快很多,而且价格上比起一直在涨价的STM系低出非常多;总的来说,STM32在国内的使用程度和受众程度是远大于ESP32的,毕竟STM32已经占据主流市场很久了,学习的资料比起ESP32起来会多,再加上一些开源问题,使用32的人群还是占据多数。2016年,乐鑫推出了ESP32,它是ESP8266的升级版本,速度更快还带有蓝牙4.2和蓝牙低功耗,价格在20元左右,在这个价格下几乎找不到对手可以“一战”。_esp8266为什么不建议使用
文章浏览阅读990次。Linux系统可以挂载多个不同接口类型的磁盘(disk),每一个磁盘又可以分成若干个分区(Partition),每个分区又可以拥有自己的文件系统类型(FileSystem)。Linux对于磁盘和分区又自己的一套标记方法。硬盘和分区的区分第一个SCSI(Small Computer System Interface)磁盘记为/dev/sda,第二个SCSI磁盘记为/dev/sdb;第一个SATA磁盘..._linux引导分区的标记可为
文章浏览阅读656次。第12课:网络游戏案例讲解上章给读者介绍关于服务器之间的通信,本章通过案例给读者介绍如何将框架跟 Photon 结合起来,实现一个网络通信的框架设计。UI 架构设计模块已经介绍过,我们的 Demo 使用的 UI 是 UGUI,简单的用几个 Button 代替 Sprite,它们的原理是一样的。我们先创建一个 UI,如下图所示:这个 UI 主要有三个关键按钮,分别是 Create Roo..._网络游戏消息结构设计案例
文章浏览阅读548次。Android 构建系统非常灵活,可让你在不修改应用核心源代码文件的情况下执行自定义构建配置。本部分将介绍 Android 构建系统的工作原理,以及它如何帮助你对多个构建配置进行自定义和自动化处理。构建过程涉及许多将你的项目转换为 Android 应用程序包 (APK) 的工具和过程。构建过程非常灵活,因此了解一些幕后发生的事情很有用。下图为Android 应用模块的构建过程。Android 应用模块的构建过程(如上图所示)遵循以下一般步骤:1、编译器将你的源代码转换为 DEX(Dalv_grade编译运行安卓
文章浏览阅读4.3k次,点赞3次,收藏6次。查看本章节查看作业目录需求说明:使用 JavaScript 中的 Date 对象,在页面上显示一个万年历。选择不同的年份和月份,在页面中显示当前月的日历实现思路:在页面中添加两个 <select> 标签,用来显示年份和月份;同时添加两个 <ul> 标签,一个用来显示星期,另一个用来显示日期在 JavaScript 脚本中动态添加年份和月份,获取当前日期的年份和月份,显示到 <select> 标签上根据 <select> 标签上显_javascript select填充月份
文章浏览阅读2.4k次。题目1174:查找第K小数时间限制:1 秒内存限制:32 兆特殊判题:否提交:5161解决:2081题目描述:查找一个数组的第K小的数,注意同样大小算一样大。 如 2 1 3 4 5 2 第三小数为3。输入:输入有多组数据。每组输入n,然后输入n个整数(1输出:输出第k小的整数_oj 第k小整数 c++
文章浏览阅读814次,点赞3次,收藏4次。我们接触到的电感器是各种各样的:有绕着圈圈的,有贴片的(0805/0603/0402/0201),还有一些是四四方方的。那么这些电感有什么差别呢?本章我们来一起看贴片电感器和磁珠,我们大多只知道磁珠是耗能元件而电感是储能元件,从原理上为什么会是这样的效果呢?它们在结构上有差别么?ps:“暗物质”看不见摸不着,据说每秒钟有几吨的暗物质穿过我们的身体,那我们是怎么知道暗物质这玩意存在的呢?_亨特h
文章浏览阅读259次。由于微信的限制,应用文件在内置浏览器中下载全部被屏蔽掉,造成很多人用微信扫描二维码下载时,界面显示一片空白,容易误导以为在下载呢。按照当前主流习惯,做一个提示用户在浏览器中打开下载的遮罩。网上很多现成的例子,解释的也很详细,但感觉没有直接上手可用的例子,因此,我打算写个示例。<!DOCTYPE html><html><head><met..._微信扫码下载apk遮罩提示
文章浏览阅读4.5k次,点赞7次,收藏10次。通过上述方法,您不仅可以解决因Docker日志文件导致的C盘空间爆满问题,还可以应用这一技巧,管理系统中的任何其他大文件或文件夹。保持C盘的清爽,让您的Windows系统运行更加流畅!_appdata\local\docker\log\host
文章浏览阅读54次。首先想到的是 js插件 ajaxSubmitForm1. http://www.cnblogs.com/DylanZ/p/6019611.html 1 function ajaxSubmitForm() { 2 var option = { 3 url : '${pageContext.request.contextPath}/userCo..._clientcallmode