React是前端组件化开发的开山鼻祖,这种开发方式完全解决了的前端组件复用的痛点。今天,就来研究一下React组件开发。html
前端同窗通常都会从Vue入门,由于Vue使用的<template>
的组件开发方式让前端人员更容易的平滑过渡。Vue的组件很简单,通常来讲,一个.vue
文件就是一个组件。就像webpack
的模块化开发,一个文件就是一个组件。咱们在使用组件时也很容易,经过 ES6 的import
导入、注册(components),就能够直接使用。前端
上面简单说了Vue的组件模式,其实,React的使用方式也差很少,毕竟是借鉴它的组件思想。最明显的差异就是声明方式,React用的是JSX语法,这让React变的很灵活。固然,使用方式的灵活也意味着使用者的脑子也要足够的灵活,从而增长了入门的难度。玩的6的人是真喜欢,未入门的是真不能理解为啥把html标签写到js里面呢。vue
好了,上面扯了那么多,下面就来一探React组件的究竟...react
最开始,让咱们先来了解一下React组件是如何定义的和分类的。webpack
React组件的声明方式有两种(React.createClass
已弃用):函数组件和class组件。它们的区别是什么呢?web
若是一个组件只根据props渲染页面,没有内部状态state,咱们彻底能够用函数组件的形式来实现。也就是说,函数组件只是简单的负责展现数据,它里面没有生命周期函数和state状态,不能作业务逻辑处理。redux
函数组件(无状态组件):设计模式
import React from "react"; function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
class组件:数组
import React, { Component } from "react"; class Welcome extends React.Component { constructor(props) { super(props); // 设置 initial state this.state = { text: props.initialValue || 'placeholder' }; // ES6 类中函数必须手动绑定 this.handleChange = this.handleChange.bind(this); } handleClick(event) { this.setState({ text: event.target.value }); } componentDidMount() { // do something } componentWillUnmount() { clearInterval(this.timer); } render() { return ( <div> <h1>Hello, {this.props.name}</h1>; <button onClick={this.handleClick}></button> </div> ) } }
注意:性能优化
React的组件分红两种类型:展现组件和容器组件。 将组件分红这两类以后,你会发现它们容易更被重用和理解。一般,展现组件负责展现内容,容器组件负责处理业务逻辑。是否是很像上面的函数组件和class组件的区别。
容器组件和展现组件名词都来自于redux中文文档。
展现组件
关注页面的展现效果(外观)
内部能够包含展现组件和容器组件,一般会包含一些本身的DOM标记和样式(style)
一般容许经过this.props.children方式来包含其余组件
对应用程序的其余部分没有依赖关系,例如Flux操做或store
不用关心数据是怎么加载和变更的
只能经过props的方式接收数据和进行回调(callback)操做
不多拥有本身的状态,即便有也是用于展现UI状态的
会被写成函数式组件除非该组件须要本身的状态,生命周期或者作一些性能优化
Example:Page、Header、Sidebar、List、UserInfo...
容器组件
关注应用的是如何工做的
内部能够包含容器组件和展现组件,但一般没有任何本身的DOM标记 (除了一些包装用的 div) ,而且从不具备任何样式
提供数据和行为给其余的展现组件或容器组件
调用Flux操做并将它们做为回调函数提供给展现组件
每每是有状态的,由于它们倾向于做为数据源
一般使用高阶组件生成,例如React Redux的connect(),Relay的createContainer()或Flux Utils的Container.create(),而不是手工编写
Example:UserPage、StoryContainer、 FollowedUserList...
如今,咱们已经知道了展现组件和容器组件的不一样点。容器组件倾向于有状态,展现组件倾向于无状态,这不是硬性规定,由于容器组件和展现组件均可以是有状态的。 一般,展现组件被容器组件包裹的,由于容器组件内能够处理业务逻辑,将处理好的数据传递给展现组件。
当不重要或说很难分清时,不要把分离容器组件和展现组件当作教条, 若是你不肯定该组件是容器组件仍是展现组件是,就暂时不要分离,写成展现组件吧。
高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而造成的设计模式。
具体而言,高阶组件是参数为组件,返回值为新组件的函数。
const EnhancedComponent = higherOrderComponent(WrappedComponent);
组件是将 props 转换为 UI,而高阶组件是将组件转换为另外一个组件。还记得上面说的容器组件吗?高阶函数和容器组件有类似之处,都能将相同的逻辑抽离出来复用,都须要包裹展现组件。
高阶组件来源于JavaScript的高阶函数—— 高阶函数就是接受函数做为输入或者输出的函数。高阶组件仅仅只是是一个接受组件组做输入并返回组件的函数。看上去并无什么,那么高阶组件能为咱们带来什么呢?首先看一下高阶组件是如何实现的,一般状况下,实现高阶组件的方式有如下三种:
操做props
const HOC = (WrappedComponent) => class WrapperComponent extends Component { render() { const newProps = { name: 'HOC' } return <WrappedComponent {...this.props} {...newProps} />; } }
咱们看到以前要传递给被包裹组件WrappedComponent的属性首先传递给了高阶组件返回的组件(WrapperComponent),这样咱们就得到了props的控制权(这也就是为何这种方法叫作属性代理)。咱们能够按照须要对传入的props进行增长、删除、修改(固然修改带来的风险须要你本身来控制)。
抽象state
属性代理的状况下,咱们能够将被包裹组件(WrappedComponent)中的状态提到包裹组件中,一个常见的例子就是实现不受控组件到受控的组件的转变。
class WrappedComponent extends Component { render() { return <input name="name" {...this.props.name} />; } } const HOC = (WrappedComponent) => class extends Component { constructor(props) { super(props); this.state = { name: '', }; this.onNameChange = this.onNameChange.bind(this); } onNameChange(event) { this.setState({ name: event.target.value, }) } render() { const newProps = { name: { value: this.state.name, onChange: this.onNameChange, }, } return <WrappedComponent {...this.props} {...newProps} />; } }
上面的例子中经过高阶组件,咱们将不受控组件(WrappedComponent)成功的转变为受控组件。
组合方式
const HoC = (WrappedComponent, LoginView) => { const WrappingComponent = () => { const {user} = this.props; if (user) { return <WrappedComponent {...this.props} /> } else { return <LoginView {...this.props} /> } }; return WrappingComponent; };
上述代码中有两个组件,WrappedComponent 和 LoginView,若是传入的props
中存在user
,则正常显示的 WrappedComponent 组件,不然显示 LoginView 组件,让用户去登陆。HoC 传递的参数能够为多个,传递多个组件定制新组件的行为,例如用户登陆状态下显示主页面,未登陆显示登陆界面;在渲染列表时,传入 List 和 Loading 组件,为新组件添加加载中的行为。
反向继承是指返回的组件去继承以前的组件
操做props和state
在某些状况下,咱们可能须要为高阶属性传入一些参数,那咱们就能够经过柯里化的形式传入参数,例如:
import React, { Component } from 'React'; const HOCFactoryFactory = (...params) => { // 能够作一些改变 params 的事 return (WrappedComponent) => { return class HOC extends Component { render() { return <WrappedComponent {...this.props} />; } } } }
能够经过下面方式使用:
HOCFactoryFactory(params)(WrappedComponent)
这种方式是否是很是相似于React-Redux
库中的connect
函数,由于connect
也是相似的一种高阶函数。反向继承不一样于属性代理的调用顺序,组件的渲染顺序是: 先WrappedComponent再WrapperComponent(执行ComponentDidMount的时间)。而卸载的顺序也是先WrappedComponent再WrapperComponent(执行ComponentWillUnmount的时间)。
写到这里算是结束了,很简略地介绍了React的组件。文章虽短,但愿能让你大概了解React组件的一些特征。最后总结一下:
React组件的声明方式用两种:函数组件和class组件。函数组件只有render一个返回函数,class组件能够有生命周期和其余业务逻辑处理函数
React组件从使用形式上分两种:展现组件和容器组件。基本原则:容器组件负责数据获取,展现组件负责根据props显示信息
React高阶组件就是参数为组件,返回值为新组件的函数。这样作的好处就是能够抽离一些相同的业务逻辑便于复用。做用相似Mixin,但避免了Mixin的反作用。