资讯专栏INFORMATION COLUMN

Semantic-UI的React实现(四):基本元素组件的共通处理(父类)实现

woshicixide / 1392人阅读

摘要:上一篇的实现三基本元素组件已经提到过,基本元素组件的实现因为没有复杂的交互,仅仅是类的编辑和组装,因此实现原理相对比较简单。事件系统的回调这个功能目前还在实现中。

上一篇(Semantic-UI的React实现(三):基本元素组件)已经提到过,基本元素组件的实现因为没有复杂的交互,仅仅是CSS类的编辑和组装,因此实现原理相对比较简单。

但简单的东西要想做的简洁,往往不简单。

抽象与封装

想要简洁高效地封装数十个基本组件,将组件的相同处理部分抽象出来是非常必要的。在ES6中js新增了class关键字(当然这只是一个语法糖,其背后的处理原理仍然是prototype那一套东西。),有了这个关键字js在抽象与封装的思想上比之前更进了一步。

当用“继承”的思想去考虑问题后,组件的共通处理很明显可以通过继承一个共同父类来完成(通常我更愿意用接口而非继承,无奈js学艺不精,不清楚接口继承如何实现)。继承以后,所有基本组件的以下处理,均可以由父类的处理完成:

编辑和组装CSS类

渲染组件本身

封装事件系统的方法回调

实现细节 编辑和组装CSS类

在系列文章二的时候有提到过,基本组件的CSS编辑和组装,在PropsHelper中实现,所有细节对外隐藏,组件仅需声明相关属性即可。如Header使用到的属性:

// 属性定义
const PROP_TYPES = PropsHelper.getDefaultPropTypes().concat([
  "size", "sub", "dividing", "floated", "aligned", "inverted", "inline", "color"
]);

这些可用属性的声明,再加上Button组件实例的props,即可编辑和组装出所需的CSS类名集合。在Header的render方法中,仅需调用:

render() {

  // 渲染元素
  let style = this.createElementStyle(this.props, PROP_TYPES) + " header";
  return super.render(style);
}

具体的生成style的细节,在Header的父类UiElement中:

/**
 * 生成元素的style
 */
createElementStyle(props, propsDef) {

  ...
  return PropsHelper.createStyle(props, propsDef) + " " + style;
}
渲染组件

渲染组件也是共通处理实现的,作为子类的基本组件,仅需调用super.render即可:

render(style, children, props) {

  return React.createElement(
    this.props.as,                // 组件的html标签(默认div)
    {
      id: this.props.id,          // 组件ID
      className: style,           // 组件class
      ...this.getEventCallback(), // 事件回调声明
      ...props                    // 组件其他props(用于生成class的props不需要了)
    },
    children ? children : this.props.children
  );
}

最开始的时候,其实并没有这个实现,各个组件的渲染过程还是留在组件各自的render中的。但随着组件的增多,发现这部分代码可重用性非常大。如果有特殊的组件不适用这个过程,直接在该组件中覆写该方法即可。这对整体代码的可维护性也有很大程度的提高。

事件系统的回调

这个功能目前还在实现中。我的目标是,任何组件仅需声明而无需在该组件内部实现回调,由公共方法来实现回调处理。如一个Button想要用onClick方法,直接声明:

但在Button组件内部无需实现onClick的回调处理。(实际上也无法实现,因为Button的render处理是在其父类UiElement中实现的)

const EVENT_CALLBACK = [
  "onKeyDown", "onKeyPress", "onKeyUp",
  "onFocus", "onBlur",
  "onChange", "onInput", "onSubmit",
  "onClick", "onContextMenu", "onDoubleClick", "onDrag", "onDragEnd", "onDragEnter",
  "onDragExit", "onDragLeave", "onDragOver", "onDragStart", "onDrop", "onMouseDown",
  "onMouseEnter", "onMouseLeave", "onMouseMove", "onMouseOut", "onMouseOver", "onMouseUp",
  "onSelect",
  "onTouchCancel", "onTouchEnd", "onTouchMove", "onTouchStart",
  "onScroll", "onWheel",
  "onLoad", "onError",
  "onTransitionEnd",
  "onAnimationStart", "onAnimationEnd", "onAnimationIteration",
];

对于事件系统的回调,在constructor中是这样定义的:

constructor(props) {
  super(props);

  let eventProps = {};
  for (let key in props) {
    if (key.indexOf("on") == 0 && EVENT_CALLBACK.indexOf(key) >= 0) {
      eventProps[key] = this.handleCallback.bind(this, key);
    }
  }

  this.eventCallbacks = eventProps;
}

这个组件传入的props中如果包含"onXXX"并且这个"onXXX"在EVENT_CALLBACK中有定义,则认为该组件声明了一个事件系统的回调,那么UiElement将绑定这个回调的具体处理。处理过程如此实现:

handleCallback(callback, e) {

  if (this.props.callback) {
    this.props.callback(e);
  }
}
回顾

在UiElement中,实现了三类公共功能供基本组件类调用:

编辑和组装CSS类

渲染组件本身

封装事件系统的方法回调

实现以后,基本组件类的相同处理均被抽离出来,仅剩下一些声明性质的代码。例如Header组件的实现被简化为:

import React from "react";

import PropsHelper from "./PropsHelper";
import UiElement from "./UiElement";

// 属性定义
const PROP_TYPES = PropsHelper.getDefaultPropTypes().concat([
  "size", "sub", "dividing", "floated", "aligned", "inverted", "inline", "color"
]);

/**
 * 标题组件
 */
class Header extends UiElement {

  // 类型定义
  static propTypes = {
    ...PropsHelper.createPropTypes(PROP_TYPES)
  };

  // 默认值定义
  static defaultProps = {
    ...PropsHelper.getDefaultPropsValue(PROP_TYPES)
  };

  /**
   * 取得渲染内容
   */
  render() {

    // 渲染元素
    let style = this.createElementStyle(this.props, PROP_TYPES) + " header";
    return super.render(style);
  }
}

export default Header;

这样的好处是显而易见的:

简化实现代码提高可阅读性

封装共通处理提高可维护性

通过方法覆写保持可扩展性

通过这几篇,基础组件的封装处理应该说完了,接下来的几篇打算说说复杂组件的实现。在完成所有组件的封装后,还打算扩展一些复杂组件的功能(代码丑,只能多实现些功能了。总之要和官方做成不一样的/(ㄒoㄒ)/~~)。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/80663.html

相关文章

  • React表单

    摘要:从服务端请求数据创建一个文件大胖分钟前天气不错啊骚胖分钟前出去玩啊老胖分钟前去哪玩啊从服务端请求数据为了页面的数据和服务端的保持联系,设置每隔五秒向服务端发生一次请求在帮顶一下事件提交表单。。。。 React表单 首先,我们需要搭建一个React环境,用来实现效果: 先安装React,这里我用的并不是最新版本的,所以需要选择一个版本: jspm install react@0.14.0...

    Coding01 评论0 收藏0
  • react组件通信实现表单提交

    摘要:组件通信实现表单提交昨晚做了一个的例子,主要实现的是提交表单实现评论的功能,在做之前先简单介绍一下。并称为前端大框架,就目前来看,尽管发布了也在今年月份发布了,更不在话下,大家要是想学习的话可以去官网学习。 react组件通信实现表单提交 昨晚做了一个react的例子,主要实现的是提交表单实现评论的功能,在做之前先简单介绍一下React。 showImg(https://segment...

    LoftySoul 评论0 收藏0
  • 玩转 React)- 创造一个新 HTML 标签

    摘要:属性是一个组件的外部输入。只会在开发模式下进行属性类型检查,当代码进行生产发布后,为了减少额外的性能开销,类型检查将会被略过。某个类的实例枚举,属性值必须为其中的某一个值。属性为一个数组,且数组中的元素必须符合指定类型。 在第二篇文章 《新型前端开发方式》 中有说到 React 有很爽的一点就是给我们一种创造 HTML 标签的能力,那么今天这篇文章就详细讲解下 React 是如何提供这...

    soasme 评论0 收藏0
  • 玩转 React)- 创造一个新 HTML 标签

    摘要:属性是一个组件的外部输入。只会在开发模式下进行属性类型检查,当代码进行生产发布后,为了减少额外的性能开销,类型检查将会被略过。某个类的实例枚举,属性值必须为其中的某一个值。属性为一个数组,且数组中的元素必须符合指定类型。 在第二篇文章 《新型前端开发方式》 中有说到 React 有很爽的一点就是给我们一种创造 HTML 标签的能力,那么今天这篇文章就详细讲解下 React 是如何提供这...

    dendoink 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<