React是前端组件化开发的开山鼻祖,这种开发方式彻底解决了的前端组件复用的痛点。今天,就来研究一下React组件开发。

前端同学一般都会从Vue入门,因为Vue使用的<template>的组件开发方式让前端人员更容易的平滑过渡。Vue的组件很简单,一般来说,一个.vue文件就是一个组件。就像webpack的模块化开发,一个文件就是一个组件。我们在使用组件时也很容易,通过 ES6 的import导入、注册(components),就可以直接使用。

上面简单说了Vue的组件模式,其实,React的使用方式也差不多,毕竟是借鉴它的组件思想。最明显的差别就是声明方式,React用的是JSX语法,这让React变的很灵活。当然,使用方式的灵活也意味着使用者的脑子也要足够的灵活,从而增加了入门的难度。玩的6的人是真喜欢,未入门的是真不能理解为啥把html标签写到js里面呢。

好了,上面扯了那么多,下面就来一探React组件的究竟...

React组件基础

最开始,让我们先来了解一下React组件是如何定义的和分类的。

组件定义

React组件的声明方式有两种(React.createClass已弃用):函数组件和class组件。它们的区别是什么呢?

如果一个组件只根据props渲染页面,没有内部状态state,我们完全可以用函数组件的形式来实现。也就是说,函数组件只是简单的负责展示数据,它里面没有生命周期函数和state状态,不能做业务逻辑处理。

函数组件(无状态组件):

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>
)
}
}

注意:

  1. React自义定的组件均为大写字母开头
  2. Props是只读的,如果要改变状态,需要使用状态提升

组件分类

React的组件分成两种类型:展示组件和容器组件。 将组件分成这两类之后,你会发现它们容易更被重用和理解。通常,展示组件负责展示内容,容器组件负责处理业务逻辑。是不是很像上面的函数组件和class组件的区别。

容器组件和展示组件名词都来自于redux中文文档。

  • 展示组件

    1. 关注页面的展示效果(外观)

    2. 内部可以包含展示组件和容器组件,通常会包含一些自己的DOM标记和样式(style)

    3. 通常允许通过this.props.children方式来包含其他组件

    4. 对应用程序的其他部分没有依赖关系,例如Flux操作或store

    5. 不用关心数据是怎么加载和变动的

    6. 只能通过props的方式接收数据和进行回调(callback)操作

    7. 很少拥有自己的状态,即使有也是用于展示UI状态的

    8. 会被写成函数式组件除非该组件需要自己的状态,生命周期或者做一些性能优化

Example:Page、Header、Sidebar、List、UserInfo...

  • 容器组件

    1. 关注应用的是如何工作的

    2. 内部可以包含容器组件和展示组件,但通常没有任何自己的DOM标记 (除了一些包装用的 div) ,并且从不具有任何样式

    3. 提供数据和行为给其他的展示组件或容器组件

    4. 调用Flux操作并将它们作为回调函数提供给展示组件

    5. 往往是有状态的,因为它们倾向于作为数据源

    6. 通常使用高阶组件生成,例如React Redux的connect(),Relay的createContainer()或Flux Utils的Container.create(),而不是手工编写

    Example:UserPage、StoryContainer、 FollowedUserList...

  • 优点

    1. 更好分离关注点。用这种方式写组件,可以更好理解你的应用程序和 UI
    2. 重用性高,展示组件可以用于多个不同数据源。
    3. 展示组件就是你的调色板,可以把他们放到单独的页面,在不影响应用程序的情况下,让设计师调整UI。
    4. 这迫使您提取诸如侧边栏,页面,上下文菜单等“布局组件”并使用this.props.children,而不是在多个容器组件中复制相同的标记和布局。

现在,我们已经知道了展示组件和容器组件的不同点。容器组件倾向于有状态,展示组件倾向于无状态,这不是硬性规定,因为容器组件和展示组件都可以是有状态的。 通常,展示组件被容器组件包裹的,因为容器组件内可以处理业务逻辑,将处理好的数据传递给展示组件。

当不重要或说很难分清时,不要把分离容器组件和展示组件当做教条, 如果你不确定该组件是容器组件还是展示组件是,就暂时不要分离,写成展示组件吧。

高阶组件

高阶组件(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的副作用。

React组件略讲的更多相关文章

  1. 从工程化角度讨论如何快速构建可靠React组件

    前言 React 的开发也已经有2年时间了,先从QQ的家校群,转成做互动直播,主要是花样直播这一块.切换过来的时候,业务非常繁忙,接手过来的业务比较凌乱,也没有任何组件复用可言. 为了提高开发效率,去 ...

  2. 如何快速构建React组件库

    前言 俗话说:"麻雀虽小,五脏俱全",搭建一个组件库,知之非难,行之不易,涉及到的技术方方面面,犹如海面风平浪静,实则暗礁险滩,处处惊险- 目前团队内已经有较为成熟的 Vue 技术 ...

  3. 解析Markdown文件生成React组件文档

    前言 最近做的项目使用了微前端框架single-spa. 对于这类微前端框架而言,通常有个utility应用,也就是公共应用,里面是各个子应用之间可以共用的一些公共组件或者方法. 对于一个团队而言,项 ...

  4. 使用reflux进行react组件之间的通信

    前言 组件之间为什么要通信?因为有依赖. 那么,作为React组件,怎么通信? React官网说, 进行 父-子 通信,可以直接pass props. 进行 子-父 通信,往父组件传给子组件的函数注入 ...

  5. 野心勃勃的React组件生命周期

    当你还在写着Angular指令,过滤器,注入,服务,提供者,视图模版的时候,是不是觉得很烦,好在这个时候,React已经神一样的出现在历史舞台. React组件    React实现了UI=Fn(St ...

  6. 推荐一款中国风React组件

    最近看这个中国风的组件在掘金也火了一段时间,看了有几天了,也体验了下,感觉还不错,所以准备来安利下 项目地址:https://github.com/zhui-team/zhui 使用手册请参考:htt ...

  7. React组件,React和生命周期

    笔记,具体可以看看这个博客: https://segmentfault.com/a/1190000004168886?utm_source=tag-newest react 的jsx document ...

  8. React 组件的生命周期API和事件处理

    一.简单记录React的组件的简洁的生命周期API: A:实例化期: 一个实例第一次被创建时所调用的API与其它后续实例被创建时所调用的API略有不同. 实例第一次被创建时会调用getDefaultP ...

  9. React 16 源码瞎几把解读 【二】 react组件的解析过程

    一.一个真正的react组件编译后长啥样? 我们瞎几把解读了react 虚拟dom对象是怎么生成的,生成了一个什么样的解构.一个react组件不光由若干个这些嵌套的虚拟dom对象组成,还包括各种生命周 ...

随机推荐

  1. c# winform用sharpGL(OpenGl)解析读取3D模型obj

    原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/11783026.html 自己写了个简单的类读取解析obj模型,使用导入类,然后new个对象,在 ...

  2. mysql实现海量数据的存储、访问的解决方案

    mysql实现海量数据的存储.访问的解决方案: mysql数据库水平切分的实现原理可分为以下几个:分库,分表,主从,集群,负载均衡器等 第1章 引言 随着互联网应用的广泛普及,海量数据的存储和访问成为 ...

  3. 前端技术之:常用webpack插件

    1.html-webpack-plugin Simplifies creation of HTML files to serve your webpack bundles.   主页地址: https ...

  4. 【原创】go语言学习(二十一)Select和线程安全

    目录 select语义介绍和使用 线程安全介绍 互斥锁介绍和实战 读写锁介绍和实战 原子操作介绍 select语义介绍和使用 1.多channel场景 A. 多个channel同时需要读取或写入,怎么 ...

  5. SpringBoot正确打日志的姿势

    前篇 Spring Boot 日志处理你还在用Logback? 本文简介 前篇侧重 Log4j2 的配置,本篇侧重统一日志处理的应用,以下包含 HTTP 请求的日志处理.Exception 异常日志处 ...

  6. 【翻译】Prometheus 2.12.0 新特性

    Prometheus 2.12.0 现在(2019.08.17)已经发布,在上个月的 2.11.0 之后又进行了一些修正和改进. 在当前的 6 周发布周期中,每一个 Prometheus 版本都有比较 ...

  7. 【Java必修课】String.intern()原来还能这么用(原理与应用)

    1 简介 String.intern()是JDK一早就提供的native方法,不由Java实现,而是底层JVM实现,这让我们对它的窥探提高了难度.特别是在Oracle收购了Sun公司后,源代码不开源了 ...

  8. MySQL8.0 redo日志系统优化

    背景 现在主流的数据库系统的故障恢复逻辑都是基于经典的ARIES协议,也就是基于undo日志+redo日志的来进行故障恢复.redo日志是物理日志,一般采用WAL(Write-Ahead-Loggin ...

  9. 小白学 Python(19):基础异常处理

    人生苦短,我选Python 前文传送门 小白学 Python(1):开篇 小白学 Python(2):基础数据类型(上) 小白学 Python(3):基础数据类型(下) 小白学 Python(4):变 ...

  10. windows使用docker运行mysql等工具(一)windows安装docker

    由于我本地的mysql版本比较低,最近想着要升级一下mysql版本,鉴于docker容器的火热,就想着在本地装个docker环境,然后下载最新版的mysql镜像,完成mysql8.0的安装.电脑是wi ...