antd源码分析之——折叠面板(collapse)

2019-08-20 admin

官方文档 https://ant.design/components…

目录

一、antd中的collapse

代码目录

1、组件结构图(♦♦♦重要)

2、源码节选:antd/components/collapse/collapse.tsx

3、源码节选:antd/components/collapse/CollapsePanel.tsx

二、RcCollapse

代码目录

1、组件内部属性结构及方法调用关系图(♦♦♦重要)

2、组件应用的设计模式(♦♦♦重要)

3、源码节选:rc-collapse/Collapse.jsx

4、源码节选:rc-collapse/panel.jsx

一、antd中的collapse

antd组件中有些使用了React 底层基础组件(查看具体列表点这里),collapse就是这种类型的组件

antd中collapse主要源码及组成结构如下,其中红色标注的Rc开头的组件是React底层基础组件

代码目录

clipboard.png

1、组件结构图:

clipboard.png

2、antd/components/collapse/collapse.tsx

export default class Collapse extends React.Component<CollapseProps, any> {
  static Panel = CollapsePanel;

  static defaultProps = {
    prefixCls: 'ant-collapse',
    bordered: true,
    openAnimation: { ...animation, appear() { } },
  };

  renderExpandIcon = () => {
    return (
      <Icon type="right" className={`arrow`} />
    );
  }

  render() {
    const { prefixCls, className = '', bordered } = this.props;
    const collapseClassName = classNames({
      [`${prefixCls}-borderless`]: !bordered,
    }, className);
    return (
      <RcCollapse
        {...this.props}
        className={collapseClassName}
        expandIcon={this.renderExpandIcon}
      />
    );
  }
}

3、antd/components/collapse/CollapsePanel.tsx

export default class CollapsePanel extends React.Component<CollapsePanelProps, {}> {
  render() {
    const { prefixCls, className = '', showArrow = true } = this.props;
    const collapsePanelClassName = classNames({
      [`${prefixCls}-no-arrow`]: !showArrow,
    }, className);
    return <RcCollapse.Panel {...this.props} className={collapsePanelClassName} />;
  }
}

二、RcCollapse

由上述Collapse源码不难看出,折叠面板组件的实现逻辑主要在RcCollapse中,下面是核心代码、组件内部属性结构及方法调用关系图

代码目录

clipboard.png

1、组件内部属性结构及方法调用关系图

clipboard.png

2、组件应用的设计模式

这个组件中主要使用里“聪明组件和傻瓜组件”模式、“组合组件”模式

a、聪明组件和傻瓜组件:

  • 遵循职责分离原则,把获取和管理数据的逻辑放在父组件,作为聪明组件;把渲染界面的逻辑放在子组件,也就是傻瓜组件

  • 聪明组件:Collapse,负责获取和管理数据

          getItems(),获取数据,将props中的数据传递给子组件CollapsePanel;
          onClickItem(),管理数据,计算active的值传递给子组件;
    
  • 傻瓜组件:Panel,只负责渲染;

          根据父组件传入的数据控制渲染逻辑,如active时的渲染效果
    
    

b、组合组件:

  • 适用场景: Collapse组件中Collapse是一个容器,包含一个或多个CollapsePanel,可以有一个(手风琴)或多个Panel展开(active),展开的样式不同与未展开
  • 一般实现: 每个Panel中传入isActive状态和onclick方法,在Panel内部实现渲染逻辑
  • 缺陷: 每个Panel中要写多个props参数 每个Panel中处理onclick的相同逻辑,重复代码,增加Panel成本高 Collapse中控制active逻辑在每次新增Panel时也要修改
  • 组合组件模式: 借助React.Children.map或React.cloneElement使列表中多个子组件的公共处理移到父组件中统一处理
  • Collapse中的实现: Collapse渲染时调用this.getItems(),在this.getItems()中使用React.Children.map配置panel的onItemClick事件和activeKey等其他属性 Panel只在点击事件时调用父组件中定义的onItemClick,没有冗余代码,降低了增加Panel的成本

PS:组件设计模式详细内容可以自行查找相关资料,推荐掘金小册《React 实战:设计模式和最佳实践》,本文部分内容摘自该文

3、rc-collapse/Collapse.jsx

class Collapse extends Component {
  constructor(props) {
    super(props);
    ……this.state = {
      ……
    };
  }

  componentWillReceiveProps(nextProps) {
    ……
  }

  onClickItem(key) {
    ……
  }

  getItems() {
    const activeKey = this.state.activeKey;
    const { prefixCls, accordion, destroyInactivePanel, expandIcon } = this.props;
    const newChildren = [];

    Children.forEach(this.props.children, (child, index) => {
      if (!child) return;
      // If there is no key provide, use the panel order as default key
      const key = child.key || String(index);
      const { header, headerClass, disabled } = child.props;
      let isActive = false;
      if (accordion) {
        isActive = activeKey[0] === key;
      } else {
        isActive = activeKey.indexOf(key) > -1;
      }

      const props = {
        ……
        openAnimation: this.state.openAnimation,
        accordion,
        children: child.props.children,
        onItemClick: disabled ? null : () => this.onClickItem(key),
        expandIcon,
      };

      newChildren.push(React.cloneElement(child, props));
    });

    return newChildren;
  }

  setActiveKey(activeKey) {
    if (!('activeKey' in this.props)) {
      this.setState({ activeKey });
    }
    this.props.onChange(this.props.accordion ? activeKey[0] : activeKey);
  }

  render() {
    const { prefixCls, className, style, accordion } = this.props;
    const collapseClassName = classNames({
      [prefixCls]: true,
      [className]: !!className,
    });
    return (
      <div className={collapseClassName} style={style} role={accordion ? 'tablist' : null}>
        {this.getItems()}
      </div>
    );
  }
}

4、rc-collapse/panel.jsx

class CollapsePanel extends Component {
  handleItemClick = () => {
    if (this.props.onItemClick) {
      this.props.onItemClick();
    }
  }

  handleKeyPress = (e) => {
    if (e.key === 'Enter' || e.keyCode === 13 || e.which === 13) {
      this.handleItemClick();
    }
  }

  render() {
    const {
      ……
    } = this.props;
    const headerCls = classNames(`${prefixCls}-header`, {
      [headerClass]: headerClass,
    });
    const itemCls = classNames({
      [`${prefixCls}-item`]: true,
      [`${prefixCls}-item-active`]: isActive,
      [`${prefixCls}-item-disabled`]: disabled,
    }, className);

    let icon = null;
    if (showArrow && typeof expandIcon === 'function') {
      icon = React.createElement(expandIcon, { ...this.props });
    }
    return (
      <div className={itemCls} style={style} id={id}>
        <div
          className={headerCls}
          onClick={this.handleItemClick}
          role={accordion ? 'tab' : 'button'}
          tabIndex={disabled ? -1 : 0}
          aria-expanded={`${isActive}`}
          onKeyPress={this.handleKeyPress}
        >
          {showArrow && (icon || <i className="arrow" />)}
          {header}
        </div>
        <Animate
          showProp="isActive"
          exclusive
          component=""
          animation={this.props.openAnimation}
        >
          <PanelContent
            prefixCls={prefixCls}
            isActive={isActive}
            destroyInactivePanel={destroyInactivePanel}
            forceRender={forceRender}
            role={accordion ? 'tabpanel' : null}
          >
            {children}
          </PanelContent>
        </Animate>
      </div>
    );
  }
}

[转载]原文链接:https://segmentfault.com/a/1190000020130570

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-73055.html

文章标题:antd源码分析之——折叠面板(collapse)

相关文章
JS教程之基础
javascript教程之什么是 JavaScript? JavaScript 被设计用来向 HTML 页面添加交互行为。JavaScript 是一种脚本语言(脚本语言是一种轻量级的编程语言)。JavaScript 由数行可执行计算机代码组...
2015-11-12
DOM之通俗易懂讲解
DOM 是所有前端开发每天打交道的东西,但是随着 jQuery 等库的出现,大大简化了 DOM 操作,导致大家慢慢的 “遗忘” 了它的本来面貌。不过,要想深入学习前端知识,对 DOM 的了解是不可或缺的,所以本文力图系统的讲解下 DOM 的...
2016-01-13
Vue.js原理分析之observer模块详解
介绍 observer是Vue核心中最重要的一个模块(个人认为),能够实现视图与数据的响应式更新,底层全凭observer的支持。 **注意:**本文是针对Vue@2.1.8进行分析 observer模块在Vue项目中的代码位置是src/c...
2017-03-16
Ajax教程之Ajax介绍
Ajax 由 HTML、JavaScript™ 技术、DHTML 和 DOM 组成,这一杰出的方法可以将笨拙的 Web 界面转化成交互性的 Ajax 应用程序。本文的作者是一位 Ajax 专家,他演示了这些技术如何协同工作 —— 从总体概述...
2015-11-12
数据格式之战:JSON vs XML
在比较JSON和XML之前,我们先来上一堂关于数据格式的简要历史(更准确的说,是关于XML的始祖): 早在1970年,IBM开发了一种叫Generalized Markup Language的标记语言,简称GML,它主要是为脚本语言定义的一...
2016-01-13
JavaScript深入之类数组对象与
类数组对象 所谓的类数组对象: 拥有一个 length 属性和若干索引属性的对象 举个例子: var array = [&#x27;name&#x27;, &#x27;age&#x27;, &#x27;sex&#x27;]; var ...
2017-05-27
javascript数据结构与算法之检索算法
查找数据有2种方式,顺序查找和二分查找。顺序查找适用于元素随机排列的列表。二分查找适用于元素已排序的列表。二分查找效率更高,但是必须是已经排好序的列表元素集合。 一:顺序查找 顺序查找是从列表的第一个元素开始对列表元素逐个进行判断,直到找到...
2017-03-22
HTML5之WebSocket入门3 -通信模型socket.io
socket.io为什么会诞生呢?请看下面文字说明。 为什么需要socket.io? node.js提供了高效的服务端运行环境,但是由于浏览器端对HTML5的支持不一,为了兼容所有浏览器,提供卓越的实时的用户体验,并且为程序员提供客户端与服...
2017-03-29
javascript动画算法实例分析
本文实例讲述了javascript动画算法。分享给大家供大家参考。具体如下: 动画算法 Linear:无缓动效果(匀速运动); Quadratic:二次方的缓动; Cubic:三次方的缓动 Quartic:四次方的缓动; Quintic:五...
2017-03-27
文字垂直滚动之javascript代码
在大型的网站新闻公告和友情链接等领域经常有这种文字或图片垂直滚动的效果,下面就介绍一下文字垂直滚动的javascript代码。 javascript代码如下: &lt;!DOCTYPE html&gt; &lt;html&gt; &lt;h...
2017-03-27
回到顶部