React组件化
来源:互联网 发布:三国志11 mac 10.12 编辑:程序博客网 时间:2024/05/16 09:05
传统的组件
传统组件是结构,样式和交互分离的,分别对应html,css和js,以一个常见的tab组件为例,我们会先构建组件的基本结构
<div> <ul> <li>Tab1</li> <li>Tab2</li> <li>Tab3</li> </ul> </div> <div class="content"> <div> tab1内容... </div> <div> tab2内容... </div> <div> tab3内容... </div> </div>
然后通过一个js的tab组件操作dom
class Tab { static defaultOptions = { classPrefix: 'tabs', activeIndex: 0, }; } constructor(options) { this.options = Object.assign(Tabs.defaultOptions, options); // 从options中定义组件属性 this.element = this.options... // 各种dom操作事件 this._initElement(); this._initTabs(); this._bindTabs(); } _initElement() { ... } _initTabs() { ... } _bindTabs() { ... } destroy() { ... }
初始化过程十分简单,传入几个参数就可以赋予交互
const tab = new Tabs({ element: ..., tabs: ... })
组件封装的基本思路就是面向对象思想,交互基本上以dom为主,逻辑上是结构(html)上需要操作哪里,我们就操作哪里
- 基本的封装性 通过原型继承来实现了组件的封装
- 简单的生命周期 最明显的两个方法constructor和destroy,代表了组件的挂载和卸载过程,但其他的过程,如组件更新的生命周期并没有体现
- 明确的数据流动 这里的数据指的是调用组件的参数,一旦确定参数的值,就会解析传进来的参数,根据参数的不同作出不同的响应,然后反映到视图上
传统组件的主要问题是逻辑一旦复杂,就存在大量的DOM操作,开发和维护成本很高。后面前端又出现了MVC的架构,View只关心怎么输出变量,于是诞生了各种模板语言,让模板本身能承载逻辑,减轻了在js中操作DOM的逻辑,不过这种组件化实现的还是字符串拼接级别的组件化。
React的组件化
Web Component通过自定义元素的方式实现组件化,React的组件元素被描述成纯粹的JSON对象,由三部分组成——属性(props),状态(state)以及生命周期方法。
React组件构建方法
React组件有三种构建方法
- React.createClass
这种方法兼容性最好
const Button = React.createClass({ getDefaultProps(){ return { color: 'blue', text: '' } }, render(){ const { color, text } = this.props; return ( // 虚拟节点 <button className = {'btn-${color}'}> <em>text</em> </button> ) } })
当另一个组件需要调用Button,就和new一个对象差不多,只需要写< Button />就会被解析成React.createElement(Button)方法来创建Button实例,这意味者在应用中调用几次Button,就会创建几次Button实例
- ES6 classes
ES6 classes的写法是通过ES6标准的类语法的方式来构建方法:
import React, { Component } from 'react'; class Button extends Component { constructor(props){ super(props); } static defaultProps = { color: 'blue', text: '' } render(){ return ( <button className={btn-${color}}> <em>{text}</em> </button> ) } }
如果我们学过面向对象的知识,就知道继承与组合的不同,他们可以用IS-A和HAS-A来区别,在实际应用React的过程中,我们极少让子类去继承功能组件。试想在UI层面小的修改就会影响到整体交互或样式,用继承来抽象太死板了。所以在React组件开发中,常用的方式是将组件拆分到合理的粒度,用组合的方式合成业务组件。
- stateless function
使用无状态函数构建的组件称为无状态组件,只传入了props和context两个参数,也就是说它不存在state,也没有生命周期方法,无状态组件不像上述两种方法在调用时会创建新实例。
function Button({ color = 'blue', text = 'Confirm'}){ return ( <button className={ btn-$color }> <em>{text}</em> </button> ) }
用React实现Tabs组件
首先,用上面第二种es6 classes简洁的方法来初始化Tabs组件的骨架
import React, { Component, PropTypes } from 'react'; class Tabs extends Component { constructor(props){ super(props) } ... render() { return <div className="ui-tabs"></div> } }
state
在使用React之前,常见的MVC框架也非常容易实现交互界面的状态管理,比如Backbone。它们将View中与界面交互的状态解耦,一般将状态放在Model中管理。当组件内部使用库内置的setState方法时,最大的表现行为是该组件会尝试重新渲染。
值得注意的是,setState是一个异步方法,一个生命周期内所有的setState方法会合并操作。
我们再来看Tabs组件的state,我们需要维护两个可能的内部状态activeIndex和prevIndex,它们分别代表当前选中tab的索引和前一次tab选中的索引。针对这点我们有两个不同的视角
- activeIndex在内部更新 当我们切换标签的时候,可以看作组件内部的交互行为,被选择后通过回调函数返回具体选择 的索引。
- activeIndex在外部更新 当我们切换tab标签时候,可以看作是组件外部在传入具体的索引,而组件就像木偶一样被操控着。
这两种情形在React组件的设计中非常常见,第一种组件写法叫做智能组件(smart component)和木偶组件(dumb component)
我们来看下Tabs组件中初始化时的实现部分
constructor(props){ super(props); const currProps = this.props; let activeIndex = 0; // 来源核心判断 if('activeIndex' in currProps){ activeIndex = currProps.activeIndex } else if('defaultActiveIndex' in currProps){ activeIndex = currProps.defaultActiveIndex; } this.state = { activeIndex, prevIndex: activeIndex } }
对于activeIndex来说,既可能来源于使用内部更新的defaultActiveIndex prop,即我们不需要外组件控制组件状态,也可能来源于需要外部更新的activeIndex prop(比如一个input选择框)
props
props是React用来让组件互相联系的一种机制,通俗说就像方法的参数一样。React的单项数据流,主要的流动管道就是props。props本身是不可变的,当我们试图改变props的原始值时,React会报出类型错误的警告,组件的props一定来自于默认属性或通过父组件传递而来,如果要渲染对props加工后的值,最简单的方法就是使用局部变量(在组件中定义的)或者直接在JSX中计算结果。
再一次仔细观察Tabs组件在Web界面的特征,会看到两个区域:切换区域和内容区域,那么我们就定义两个子组件,其中TabNav组件对应切换区域,TabContent组件对应内容区域。在Tabs组件中只显示定义内容区域的子组件集合,头部区域对应内部区域每一个TabPane组件的props,让其在TabNav组件内拼装
<Tabs classfix={'tabs'} defaultActiveIndex={0}> <TabPane key={0} tab={'Tab 1'}>第一个Tab里的内容</TabPane> <TabPane key={1} tab={'Tab 2'}>第二个Tab里的内容</TabPane> <TabPane key={2} tab={'Tab 3'}>第三个Tab里的内容</TabPane> </Tabs>
基本的结构确定之后,只有两个props放在Tabs组件上,而其他参数直接放到TabPane组件中,由它的父组件TabContent隐式对TabPane组件拼装。渲染TabPane组件的方法如下:
getTabPanes(){ const { classPrefix, activeIndex, panels, isActive } = this.props; return React.children.map(panels, (child)=>{ if(!child) { return; } //将字符串转成10进制数字 const order = parseInt(child.props.order, 10); const isActive = activeIndex === order; return React.cloneElement(child, { classPrefix, isActive, children: child.props.children, key: 'tabpane-${order}', }) }) }
上述代码讲述了子组件集合是怎么渲染的,通过React.Children.map方法遍历子组件,将order(渲染顺序),isActive(是否激活tab),children(Tabs组件中传入的children)和key利用React的cloneElement方法克隆到TabPane组件中,最后返回这个TabPane组件集合。
其中React.children是React官方提供的一系列children的方法,就像js中提供给数组的方法一样。
最后,TabContent组件的render方法只需要调用getTabPanes方法即可渲染。
在TabPane组件上,除了可以传递字符串,还可以直接传入DOM节点
<TabPane order="0" tab={<span><i className=""></i></span>} 第一个Tab里的内容 </TabPane>
- React组件化
- React 组件化
- React 组件
- React组件
- react 组件
- React组件
- React 组件
- react 组件
- react组件
- react 组件
- React组件
- react---组件
- React Native 之 组件化开发
- React学习之路----3(组件化)
- 前端开发之React组件化知识
- react demo3 (自定义react组件)
- React 7 React高级组件
- react组件生命周期过程
- Android 系统默认参数的修改(二)
- Android Camera sensor…
- Android操作系统11种传感器介绍
- Android指南针app的实现原理总结
- Ubuntu 12.04 打开开机…
- React组件化
- r debug方法
- 联芯L1860C平台相关Android源码调…
- Andrpid评测分析-安兔兔V6.0技术解…
- zip error: Invalid&nbs…
- 阿里YunOS和Android的关系(转载)
- Android 不支持Linux应用SYSV…
- zip error: Invalid&nbs…
- EL表达式详解