ant design pro 代码学习(七) ----- 组件封装(登录模块)

2023-11-20

  以登录模块为例,对ant design pro的组件封装进行相关分析。登录模块包含基础组件的封装、组件按模块划分、同类组件通过配置文件生成、跨层级组件直接数据通信等,相对来说还是具有一定的代表性。

1、登录模块流程图

  首先,全局了解一下登录模块的总体流程。如下图所示。该流程图主要分两部分:1、页面布局;2、组件封装。黄色实线表示页面中组件的引用。下边会对基础组件分析,以及多层级组件直接的数据处理。

image

  由上图可知,所有的元素都是作为Login组件的children存在,依次包含Tab、UserName、Password、Mobile、Captcha、Submit。为了便于组件间关系更加明确,所有这些子组件都封装在Login组件中。

const {Tab, UserName, Password, Mobile, Captcha, Submit} = Login;

  由以上介绍可知,Login作为其他组件的父组件。涉及到跨层数据通信,子组件可以通过context属性对Login的数据进行操作。


2、跨层组件数据模型

  为便于对后续跨组件间数据通信有更好的了解,首先分析下跨层组件的数据模型。由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,
        });
      },
    };
  }
  • tabUtil:提供两个方法。addTab方法将参数id添加到state.tabs(Array)中,最终的目的是根据state.tabs是否为空来判断是否渲染Tab组件。removeTab方法从state.tabs中剔除参数id。
  • form:引用Login组件的form属性【Form.create()(Login)】,便于在后续子组件中调用form的系列方法,对子组件表单数据操作(数据绑定);
  • updateActive:
      1. 传递参数activeItem,根据Login的state属性type、active判断。active[type]是否存在,如果存在,则将activeItem添加到active[type]。如果不存在,则active[type]设置为Array属性,并将activeItem添加进去。
      2. 结合具体页面,type有两种类型account、 mobile,依次对应账户注册、手机注册。active实际的作用是,将账户注册、手机注册各自的表单元素分类,便于登录数据校验和数据提交。传递的activeItem参数,实际上是表单元素的name值。

  childContextTypes对应的以上三个属性在组件中会被调用,子组件通过调用这些方法和属性,来改变父组件的状态。

3、LoginTab组件

  LoginTab组件的处理比较简单,主要是以下三个部分:

3.1 静态属性

  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,
};

3.2 生命周期

  LoginTab组件调用了生命周期函数componentWillMount,其目的是调用父组件中context返回的addTab方法。传递的参数,是生成的唯一字符串(generateId方法返回)。

 componentWillMount() {
    if (this.context.tabUtil) {
      this.context.tabUtil.addTab(this.uniqueId);
    }
  }

3.3 render方法

   render方法返回TabPane组件(引自 ant design Tab组件)。其中将父组件传递过来的prop全部继承,包含子组件。

render() {
    return <TabPane {...this.props} />;
  }

4、loginItem组件

4.1 generator生成器

   以下是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)来区分各自的表单字段,便于数据校验和数据提交。

4.2 组件映射文件map.js

  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!',
      },
    ],
  }
}

4.3 遍历配置文件、生成多个组件

  通过循环遍历配置文件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);
});

4.4 总结

   表单元素通过统一的配置文件map.js形成集合,循环遍历配置文件map.js,生成对应的表单元素组件。其中表单元素组件的最终的展现形式取决于配置文件中的component属性;结合表单元素引用时的动态属性(onChange、defaultValue、name等)和配置文件中的静态属性(props),确定该组件最终的属性;结合表单元素引用时的rules属性和配置文件的rules属性,确定最终的校验规则。其中props、rules都符合ant design组件库中相关组件的配置属性。(文档流)

   所有的表单组件作为Login的子组件,通过调用context属性对数据进行关联。调用form属性中的getFieldDecorator对表单元素进行数据绑定。调用updateActive对表单元素根据tab类型进行分类。(数据流)


5、Login组件

5.1 数据流的定义及声明

  Login组件作为其他子组件的父组件,通过childContextTypes、getChildContext完成与子组件的数据交互(当然子组件中需要声明),关于数据流部分,已在上文中各个模块处都有详解,此处不再赘述。

5.2 事件处理

  处理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中定义)。

5.3 render方法

  其中,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);
      }
    });

5.4 组件统一管理

  其中在Login组件中由如下代码,其目的是,将LoginItem中的表单元素集合统一配置在Login组件,便于组件间管理。其中LoginSubmit组件较为简单,就是对ant design中Button的封装,此处不再赘述。

Login.Tab = LoginTab;
Login.Submit = LoginSubmit;
Object.keys(LoginItem).forEach(item => {
  Login[item] = LoginItem[item];
});

6、 Login组件间的数据流

  由以上分析可知,Login组件间的数据流如下图所示。

image


上一篇:ant design pro 代码学习(六) ----- 知识点总结2
开篇:ant design pro 代码学习(一) ----- 路由分析


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ant design pro 代码学习(七) ----- 组件封装(登录模块) 的相关文章

  • Docker进阶学习: swarm扩展学习

    swarm 集群的管理和编排 docker可初始化一个 swarm 集群 其他节点可加入 manager worker Node 就是一个docker节点 多个节点组成了一个网络集群 manager worker Service 服务 可管
  • 交易的本质 什么样的信仰,决定什么样的交易人生

    什么样的信仰 决定什么样的生活 同样 什么样的理念 也决定了什么样的交易 多数的交易员都把能否在交易市场稳定化盈利归结于自己从事这个行业的时间 通常很多人会说五年入门 十年小成等等 好像只要坚持个五年十年就能够在交易市场找到自己的位置 你信

随机推荐

  • JAVA WEB 中间件为SERVLET(四)

    写一个用户登录部署到tomcat 本地 先找到一个模板 HTML代码复制到本地的项目index jsp中 这个登录模板包含一个JSP 一个JS 三个CSS等文件 这个是index jsp代码
  • 解决git commit 报错WARNING: Block comments use a trailing */ on a separate line

    首先用一段报错的code作为示例 struct ctrl opt dtg pt IP DTG 0xf8 0x01 IP DTG 0xf0 0x00 git commit会报错 WARNING Block comments should al
  • 办公小技巧:VSCode无法安装插件怎么办?

    Visual Studio Code是Microsoft开发的一款开源免费的现代化轻量级代码编辑器 它体积小 启动快 系统内存占用率低非常适合搭建IDE VSCode不仅支持几乎所有主流的开发语言的语法高亮 智能代码补全 自定义热键 括号匹
  • 解决 qml module “QtQuick“ is not installed问题

    在编写QWidget QML 程序上 编译通过运行没有问题 但是点击加载qml窗口时 软件运行时提示module QtQuick Window is not installed 这个错误问题 其实是缺少于是把qt 安装目录下的qml模块相关
  • C语言 命名和关键字

    变量的命名规则 变量名可以由字母 数字和 下划线 组合而成 变量名不能包含除 以外的任何特殊字符 如 逗号 空格等 变量名必须以字母或 下划线 开头 变量名不能包含空白字符 换行符 空格和制表符称为空白字符 C 语言中的某些词 例如 int
  • SSH框架相关准备与入门学习

    最近开始学习java web开发 记录一下学习的过程 主要分为三个步骤 1 基础 java Mysql入门学习 2 中级 html css javascipt servlet jsp入门学习 推荐韩顺平相关视频 w3cshool在线教程 一
  • golang sleep

    golang的休眠可以使用time包中的sleep 函数原型为 func Sleep d Duration 其中的Duration定义为 type Duration int64 Duration的单位为 nanosecond 为了便于使用
  • python——selenium

    一 Selenium Python环境搭建及配置 1 1 selenium 介绍 selenium 是一个 web 的自动化测试工具 不少学习功能自动化的同学开始首选 selenium 因为它相比 QTP 有诸多有点 免费 也不用再为破解
  • cpolar内网穿透+ EasyImage组合,自建一个图床网站

    文章目录 1 前言 2 EasyImage网站搭建 2 1 EasyImage下载和安装 2 2 EasyImage网页测试 2 3 cpolar的安装和注册 3 本地网页发布 3 1 Cpolar云端设置 3 2 Cpolar内网穿透本地
  • 【马士兵】Python基础--15

    Python基础 15 文章目录 Python基础 15 编程思想 类与对象 类的创建 对象的创建 类属性 类方法 静态方法 动态绑定属性和方法 知识点总结 编程思想 类与对象 python中一切皆对象 类的创建 类的名称由一个或多个单词组
  • 【SpringCloud】SpringAMQP总结

    文章目录 1 AMQP 2 基本消息模型队列 3 WorkQueue模型 4 发布订阅模型 5 发布订阅 Fanout Exchange 6 发布订阅 DirectExchange 7 发布订阅 TopicExchange 8 消息转换器
  • 迁移学习 & 凯明初始化

    前言 这一章其实就是之前没做完的事 来补一下 两者其实没啥关系 迁移学习 以下内容学习自迁移学习 斯坦福21秋季 实用机器学习中文版 迁移学习包括什么 feature extraction train a model on a relate
  • 由于缺少调试目标 E:a\b\c\串口配置工具\bin\Debug\串口配置工具.exe“,visual Studio无法开始调试。请生成项目并重试,或者相应OutputPath和AssemblyNa

    最近做一个窗体程序时候出现这个错误 我的项目名称是串口配置工具 建议为英文来命名 项目名称下面有这两个 发现 没有这个串口配置工具 exe 然后再这个 这里面发现这个串口配置工具 exe 最后直接 exe文件把这个复制到 项目名称 bin
  • C++基础——const成员函数

    目录 一 Const成员函数 1 定义 2 格式 3 代码示例 h文件 definition cpp文件 特性 例 那么const对象既可以调用非const型成员函数吗 问题3 const成员函数内可以调用其它的非const成员函数吗 问题
  • 手机运行python 神器,pydroid3 包含库的版本

    初次安装pydroid 或者qpython的同学运行爬虫时是不是蛋疼的一比 lxml根本装不了 虽然可以下载whl折腾 可是也很麻烦 后来我不死心 终于找到了包含库的版本 只有pydroid 64位 https lanzous com id
  • msa2000映射到服务器,HPmsa2000i官方详细的设置操作流程步骤.doc

    HPmsa2000i官方详细的设置操作流程步骤 从本地管理主机登录进入 SMU 如要从本地管理主机登录进入 SMU 在网络浏览器的地址栏中 键入某个控制器机柜的以太网管理端口的 IP 地址 然后按Enter 此时显示 SMU Login 页
  • IDEA java.lang.NullPointerException (no error message)

    今天在不停启动debug 停止debug后无法再启动debug 提示java lang NullPointerException no error message 经百度 删除 project下 gradle无效 恢复代码后无效 且未更改配
  • 【C语言】合并两个数组,降序排列并删除重复元素(通俗易懂)

    问题描述 试着写一个程序 具体内容如下 建立两个整型数组 int n scanf d n int a n 将其合并 对他们进行降序排序 去掉相同项 输出处理过后的数组 输入形式 首先第一行输入第一个数组中的长度n 然后输入n个整型数 然后在
  • MYSQL进阶-msql日志-慢查询日志

    2 慢查询日志 慢查询日志主要用来记录执行时间超过设置的某个时长的SQL语句 能够帮助数据库维护人员找出执行时间比较长 执行效率比较低的SQL语句 并对这些SQL语句进行针对性优化 2 1 开启慢查询日志 可以在my cnf文件或者my i
  • ant design pro 代码学习(七) ----- 组件封装(登录模块)

    以登录模块为例 对ant design pro的组件封装进行相关分析 登录模块包含基础组件的封装 组件按模块划分 同类组件通过配置文件生成 跨层级组件直接数据通信等 相对来说还是具有一定的代表性 1 登录模块流程图 首先 全局了解一下登录模