React中组件的写法有哪些
内容提要
元素与组件 Element & Component
函数定义与类定义组件 Functional & Class
展示与容器组件 Presentational & Container
有状态与无状态组件 Stateful & Stateless
受控与非受控组件 Controlled & Uncontrolled
组合与继承 Composition & Inheritance
元素与组件 Element & Component
元素
元素是构建React应用的最小单位。元素所描述的也就是你在浏览器中能够看到的东西。根据我们在上节课中讲到的内容,我们在编写React代码时一般用JSX来描述React元素。
在作用上,我们可以把React元素理解为DOM元素;但实际上,React元素只是JS当中普通的对象。React内部实现了一套叫做React DOM的东西,或者我们称之为Virtual DOM也就是虚拟DOM.通过一个树状结构的JS对象来模拟DOM树。
说到这里我们可以稍微讲一下,React为什么会有这一层虚拟DOM呢?在课程介绍中我们曾经提到过,React很快、很轻。它之所以快就是因为这一套虚拟DOM的存在,React内部还实现了一个低复杂度高效率的Diff算法,不同于以往框架,例如angular使用的脏检查。在应用的数据改变之后,React会尽力少地比较,然后根据虚拟DOM只改变真实DOM中需要被改变的部分。React也藉此实现了它的高效率,高性能。
当然这不是虚拟DOM唯一的意义,通过这一层单独抽象的逻辑让React有了无限的可能,就比如react native的实现,可以让你只掌握JS的知识也能在其他平台系统上开发应用,而不只是写网页,甚至是之后会出现的React VR或者React物联网等等别的实现。
话说回来,元素也就是React DOM之中描述UI界面的最小单位。刚才我们说到了,元素其实就是普通的JS对象。不过我们用JSX来描述React元素在理解上可能有些困难,事实上,我们也可以不使用JSX来描述:
const element = <h1>Hello, world</h1>; // 用JSX描述就相当于是调用React的方法创建了一个对象 const element = React.createElement('h1', null, 'Hello, world');
组件
要注意到,在React当中元素和组件是两个不同的概念,之所以在前面讲了这么多,就是担心大家不小心会混淆这两个概念。首先我们需要明确的是,组件是构建在元素的基础之上的。
React官方对组件的定义呢,是指在UI界面中,可以被独立划分的、可复用的、独立的模块。其实就类似于JS当中对function函数的定义,它一般会接收一个名为props的输入,然后返回相应的React元素,再交给ReactDOM,最后渲染到屏幕上。
函数定义与类定义组件 Functional & Class
新版本的React里提供了两种定义组件的方法。当然之前的React.createClass也可以继续用,不过我们在这里先不纳入我们讨论的范围。
第一种函数定义组件,非常简单啦,我们只需要定义一个接收props传值,返回React元素的方法即可:
function Title(props) { return <h1>Hello, {props.name}</h1>; }
甚至使用ES6的箭头函数简写之后可以变成这样:
const Title = props => <h1>Hello, {props.name}</h1>
第二种是类定义组件,也就是使用ES6中新引入的类的概念来定义React组件:
class Title extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } }
之后呢,根据我们在上一节课中了解到的,组件在定义好之后,可以通过JSX描述的方式被引用,组件之间也可以相互嵌套和组合。
展示与容器组件 Presentational & Container
接下来我们还会介绍一些更深入的关于组件概念,现在听起来可能会比较抽象枯燥,不过接下来要介绍的这几个概念在之后的课程中都是会被应用到的,同学们也可以根据自己的实际情况,在学习完后续的课程之后,再返回来听听看,相信一定会对你理解React有所帮助。
首先是最重要的一组概念:展示组件与容器组件。同样,在课程介绍中我们提到的,React并不是传统的MVVM框架,它只是在V层,视图层上下功夫。同学们应该对MVVM或MVC都有所了解,那么既然我们的框架现在只有V层的话,在实际开发中应该如何处理数据与视图的关系呢?
为了解决React只有V层的这个问题,更好地区分我们的代码逻辑,展示组件与容器组件这一对概念就被引入了。这同样也是我们在开发React应用时的最佳实践。
我们还是先看一个具体的例子来解释这两个概念:
class CommentList extends React.Component { constructor(props) { super(props) this.state = { comments: [] } } componentDidMount() { $.ajax({ url: "/my-comments.json", dataType: 'json', success: function(comments) { this.setState({comments: comments}) }.bind(this) }) } renderComment({body, author}) { return <li>{body}—{author}</li> } render() { return <ul> {this.state.comments.map(this.renderComment)} </ul> } }
这是一个回复列表组件,乍看上去很正常也很合理。但实际上在开发React应用时,我们应该避免写出这样的组件,因为这类组件担负的功能太多了。它只是一个单一的组件,但需要同时负责初始化state,通过ajax获取服务器数据,渲染列表内容,在实际应用中,可能还会有更多的功能依赖。这样,在后续维护的时候,不管是我们要修改服务器数据交互还是列表样式内容,都需要去修改同一个组件,逻辑严重耦合,多个功能在同一个组件中维护也不利于团队协作。
通过应用展示组件与容器组件的概念,我们可以把上述的单一组件重构为一个展示回复列表组件和回复列表容器:
// 展示组件 class CommentList extends React.Component { constructor(props) { super(props); } renderComment({body, author}) { return <li>{body}—{author}</li>; } render() { return <ul> {this.props.comments.map(this.renderComment)} </ul>; } } // 容器组件 class CommentListContainer extends React.Component { constructor() { super() this.state = { comments: [] } } componentDidMount() { $.ajax({ url: "/my-comments.json", dataType: 'json', success: function(comments) { this.setState({comments: comments}) }.bind(this) }) } render() { return <CommentList comments={this.state.comments} /> } }
像这样回复列表如何展示与如何获取回复数据的逻辑就被分离到两个组件当中了。我们再来明确一下展示组件和容器组件的概念:
展示组件
主要负责组件内容如何展示
从props接收父组件传递来的数据
大多数情况可以通过函数定义组件声明
容器组件
主要关注组件数据如何交互
拥有自身的state,从服务器获取数据,或与redux等其他数据处理模块协作
需要通过类定义组件声明,并包含生命周期函数和其他附加方法
那么这样写具体有什么好处呢?
解耦了界面和数据的逻辑
更好的可复用性,比如同一个回复列表展示组件可以套用不同数据源的容器组件
利于团队协作,一个人负责界面结构,一个人负责数据交互
有状态与无状态组件 Stateful & Stateless
有状态组件
意思是这个组件能够获取储存改变应用或组件本身的状态数据,在React当中也就是state,一些比较明显的特征是我们可以在这样的组件当中看到对this.state的初始化,或this.setState方法的调用等等。
无状态组件
这样的组件一般只接收来自其他组件的数据。一般这样的组件中只能看到对this.props的调用,通常可以用函数定义组件的方式声明。它本身不会掌握应用的状态数据,即使触发事件,也是通过事件处理函数传递到其他有状态组件当中再对state进行操作。
我们还是来看具体的例子比较能清楚地说明问题,与此同时,我们已经介绍了三组概念,为了防止混淆,我这里特意使用了两个展示组件来做示例,其中一个是有状态组件,另一个是无状态组件,也是为了证明,并不是所有的展示组件都是无状态组件,所有的容器组件都是有状态组件。再次强调一下,这是两组不同的概念,以及对组件不同角度的划分方式。
//Stateful Component class StatefulLink extends React.Component { constructor(props) { super(props) this.state = { active: false } } handleClick() { this.setState({ active: !this.state.active }) } render() { return <a style={{ color: this.state.active ? 'red' : 'black' }} onClick={this.handleClick.bind(this)} > Stateful Link </a> } } // Stateless Component class StatelessLink extends React.Component { constructor(props) { super(props) } handleClick() { this.props.handleClick(this.props.router) } render() { const active = this.props.activeRouter === this.props.router return ( <li> <a style={{ color: active ? 'red' : 'black' }} onClick={this.handleClick.bind(this)} > Stateless Link </a> </li> ) } } class Nav extends React.Component { constructor() { super() this.state={activeRouter: 'home'} } handleSwitch(router) { this.setState({activeRouter: router}) } render() { return ( <ul> <StatelessLink activeRouter={this.state.activeRouter} router='home' handleClick={this.handleSwitch.bind(this)} /> <StatelessLink activeRouter={this.state.activeRouter} router='blog' handleClick={this.handleSwitch.bind(this)} /> <StatelessLink activeRouter={this.state.activeRouter} router='about' handleClick={this.handleSwitch.bind(this)} /> </ul> ) } }
上述的例子可能稍有些复杂,事实上,在React的实际开发当中,我们编写的组件大部分都是无状态组件。毕竟React的主要作用是编写用户界面。再加上ES6的新特性,绝大多数的无状态组件都可以通过箭头函数简写成类似下面这样:
/* function SimpleButton(props) { return <button>{props.text}</button> } */ const SimpleButton = props => <button>{props.text}</button>
受控与非受控组件 Controlled & Uncontrolled
受控组件
一般涉及到表单元素时我们才会使用这种分类方法,在后面一节课程表单及事件处理中我们还会再次谈论到这个话题。受控组件的值由props或state传入,用户在元素上交互或输入内容会引起应用state的改变。在state改变之后重新渲染组件,我们才能在页面中看到元素中值的变化,假如组件没有绑定事件处理函数改变state,用户的输入是不会起到任何效果的,这也就是“受控”的含义所在。
非受控组件
类似于传统的DOM表单控件,用户输入不会直接引起应用state的变化,我们也不会直接为非受控组件传入值。想要获取非受控组件,我们需要使用一个特殊的ref属性,同样也可以使用defaultValue属性来为其指定一次性的默认值。
我们还是来看具体的例子:
class ControlledInput extends React.Component { constructor() { super() this.state = {value: 'Please type here...'} } handleChange(event) { console.log('Controlled change:',event.target.value) this.setState({value: event.target.value}) } render() { return ( <label> Controlled Component: <input type="text" value={this.state.value} onChange={(e) => this.handleChange(e)} /> </label> ); } } class UncontrolledInput extends React.Component { constructor() { super(); } handleChange() { console.log('Uncontrolled change:',this.input.value); } render() { return ( <label> Uncontrolled Component: <input type="text" defaultValue='Please type here...' ref={(input) => this.input = input} onChange={() =>this.handleChange()} /> </label> ); } }
通常情况下,React当中所有的表单控件都需要是受控组件。但正如我们对受控组件的定义,想让受控组件正常工作,每一个受控组件我们都需要为其编写事件处理函数,有的时候确实会很烦人,比方说一个注册表单你需要写出所有验证姓名电话邮箱验证码的逻辑,当然也有一些小技巧可以让同一个事件处理函数应用在多个表单组件上,但生产开发中并没有多大实际意义。更有可能我们是在对已有的项目进行重构,除了React之外还有一些别的库需要和表单交互,这时候使用非受控组件可能会更方便一些。
组合与继承 Composition & Inheritance
前面我们已经提到了,React当中的组件是通过嵌套或组合的方式实现组件代码复用的。通过props传值和组合使用组件几乎可以满足所有场景下的需求。这样也更符合组件化的理念,就好像使用互相嵌套的DOM元素一样使用React的组件,并不需要引入继承的概念。
当然也不是说我们的代码不能这么写,来看下面这个例子:
// Inheritance class InheritedButton extends React.Component { constructor() { super() this.state = { color: 'red' } } render() { return ( <button style={{backgroundColor: this.state.color}} class='react-button'>Inherited Button</button> ) } } class BlueButton extends InheritedButton { constructor() { super() this.state = { color: '#0078e7' } } } // Composition const CompositedButton = props => <button style={{backgroundColor:props.color}}>Composited Button</button> const YellowButton = () => <CompositedButton color='#ffeb3b' />
但继承的写法并不符合React的理念。在React当中props其实是非常强大的,props几乎可以传入任何东西,变量、函数、甚至是组件本身:
function SplitPane(props) { return ( <p className="SplitPane"> <p className="SplitPane-left"> {props.left} </p> <p className="SplitPane-right"> {props.right} </p> </p> ); } function App() { return ( <SplitPane left={ <Contacts /> } right={ <Chat /> } /> ); }
以上是React中组件的写法有哪些的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

如何利用React和WebSocket构建实时聊天应用引言:随着互联网的快速发展,实时通讯越来越受到人们的关注。实时聊天应用已经成为现代社交和工作生活中不可或缺的一部分。本文将介绍如何利用React和WebSocket构建一个简单的实时聊天应用,并提供具体的代码示例。一、技术准备在开始构建实时聊天应用之前,我们需要准备以下技术和工具:React:一个用于构建

React前后端分离指南:如何实现前后端的解耦和独立部署,需要具体代码示例在当今的Web开发环境中,前后端分离已经成为一种趋势。通过将前端和后端代码分开,可以使得开发工作更加灵活、高效,并且方便进行团队协作。本文将介绍如何使用React实现前后端分离,从而实现解耦和独立部署的目标。首先,我们需要理解什么是前后端分离。传统的Web开发模式中,前端和后端是耦合在

不少用户在玩win10的的一些游戏的时候总是会遇到一些问题,比如说卡屏和花屏等等情况,这个时候我们是可以采用打开directplay这个功能来解决的,而且功能的操作方法也很简单。win10旧版组件directplay怎么安装1、在搜索框里面输入“控制面板”然后打开2、查看方式选择大图标3、找到“程序和功能”4、点击左侧的启用或关闭win功能5、选择旧版这里的勾选上就可以了

如何利用React和Flask构建简单易用的网络应用引言:随着互联网的发展,网络应用的需求也越来越多样化和复杂化。为了满足用户对于易用性和性能的要求,使用现代化的技术栈来构建网络应用变得越来越重要。React和Flask是两种在前端和后端开发中非常受欢迎的框架,它们可以很好的结合在一起,用来构建简单易用的网络应用。本文将详细介绍如何利用React和Flask

如何利用React和RabbitMQ构建可靠的消息传递应用引言:现代化的应用程序需要支持可靠的消息传递,以实现实时更新和数据同步等功能。React是一种流行的JavaScript库,用于构建用户界面,而RabbitMQ是一种可靠的消息传递中间件。本文将介绍如何结合React和RabbitMQ构建可靠的消息传递应用,并提供具体的代码示例。RabbitMQ概述:

React代码调试指南:如何快速定位和解决前端bug引言:在开发React应用程序时,经常会遇到各种各样的bug,这些bug可能使应用程序崩溃或导致不正确的行为。因此,掌握调试技巧是每个React开发者必备的能力。本文将介绍一些定位和解决前端bug的实用技巧,并提供具体的代码示例,帮助读者快速定位和解决React应用程序中的bug。一、调试工具的选择:在Re

如何利用React和GoogleBigQuery构建快速的数据分析应用引言:在当今信息爆炸的时代,数据分析已经成为了各个行业中不可或缺的环节。而其中,构建快速、高效的数据分析应用则成为了许多企业和个人追求的目标。本文将介绍如何利用React和GoogleBigQuery结合起来构建快速的数据分析应用,并提供详细的代码示例。一、概述React是一个用于构建

ReactRouter使用指南:如何实现前端路由控制随着单页应用的流行,前端路由成为了一个不可忽视的重要部分。ReactRouter作为React生态系统中最受欢迎的路由库,提供了丰富的功能和易用的API,使得前端路由的实现变得非常简单和灵活。本文将介绍ReactRouter的使用方法,并提供一些具体的代码示例。安装ReactRouter首先,我们需
