摘要:用这种方式创建组件时,并没有对内部的函数,进行绑定,所以如果你想让函数在回调中保持正确的,就要手动对需要的函数进行绑定,如上面的,在构造函数中对进行了绑定。
当我们谈起React的时候,多半会将注意力集中在组件之上,思考如何将页面划分成一个个组件,以及如何编写可复用的组件。但对于接触React不久,还没有真正用它做一个完整项目的人来说,理解如何创建一个组件也并不那么简单。在最开始的时候我以为创建组件只需要调用createClass这个api就可以了;但学习了ES6的语法后,又知道了可以利用继承,通过extends React.component来创建组件;后来在阅读别人代码的时候又发现了PureComponent以及完全没有继承,仅仅通过返回JSX语句的方式创建组件的方式。下面这篇文章,就将逐一介绍这几种创建组件的方法,分析其特点,以及如何选择使用哪一种方式创建组件。
几种方法 1.createClass如果你还没有使用ES6语法,那么定义组件,只能使用React.createClass这个helper来创建组件,下面是一段示例:
var React = require("react"); var Greeting = React.createClass({ propTypes: { name: React.PropTypes.string //属性校验 }, getDefaultProps: function() { return { name: "Mary" //默认属性值 }; }, getInitialState: function() { return {count: this.props.initialCount}; //初始化state }, handleClick: function() { //用户点击事件的处理函数 }, render: function() { returnHello, {this.props.name}
; } }); module.exports = Greeting;
这段代码,包含了组件的几个关键组成部分,这种方式下,组件的props、state等都是以对象属性的方式组合在一起,其中默认属props和初始state都是返回对象的函数,propTypes则是个对象。这里还有一个值得注意的事情是,在createClass中,React对属性中的所有函数都进行了this绑定,也就是如上面的hanleClick其实相当于handleClick.bind(this) 。
2.component因为ES6对类和继承有语法级别的支持,所以用ES6创建组件的方式更加优雅,下面是示例:
import React from "react"; class Greeting extends React.Component { constructor(props) { super(props); this.state = {count: props.initialCount}; this.handleClick = this.handleClick.bind(this); } //static defaultProps = { // name: "Mary" //定义defaultprops的另一种方式 //} //static propTypes = { //name: React.PropTypes.string //} handleClick() { //点击事件的处理函数 } render() { returnHello, {this.props.name}
; } } Greeting.propTypes = { name: React.PropTypes.string }; Greeting.defaultProps = { name: "Mary" }; export default Greating;
可以看到Greeting继承自React.component,在构造函数中,通过super()来调用父类的构造函数,同时我们看到组件的state是通过在构造函数中对this.state进行赋值实现,而组件的props是在类Greeting上创建的属性,如果你对类的属性和对象的属性的区别有所了解的话,大概能理解为什么会这么做。对于组件来说,组件的props是父组件通过调用子组件向子组件传递的,子组件内部不应该对props进行修改,它更像是所有子组件实例共享的状态,不会因为子组件内部操作而改变,因此将props定义为类Greeting的属性更为合理,而在面向对象的语法中类的属性通常被称作静态(static)属性,这也是为什么props还可以像上面注释掉的方式来定义。对于Greeting类的一个实例对象的state,它是组件对象内部维持的状态,通过用户操作会修改这些状态,每个实例的state也可能不同,彼此间不互相影响,因此通过this.state来设置。
用这种方式创建组件时,React并没有对内部的函数,进行this绑定,所以如果你想让函数在回调中保持正确的this,就要手动对需要的函数进行this绑定,如上面的handleClick,在构造函数中对this 进行了绑定。
3.PureComponet我们知道,当组件的props或者state发生变化的时候:React会对组件当前的Props和State分别与nextProps和nextState进行比较,当发现变化时,就会对当前组件以及子组件进行重新渲染,否则就不渲染。有时候为了避免组件进行不必要的重新渲染,我们通过定义shouldComponentUpdate来优化性能。例如如下代码:
class CounterButton extends React.Component { constructor(props) { super(props); this.state = {count: 1}; } shouldComponentUpdate(nextProps, nextState) { if (this.props.color !== nextProps.color) { return true; } if (this.state.count !== nextState.count) { return true; } return false; } render() { return ( ); } }
shouldComponentUpdate通过判断props.color和state.count是否发生变化来决定需不需要重新渲染组件,当然有时候这种简单的判断,显得有些多余和样板化,于是React就提供了PureComponent来自动帮我们做这件事,这样就不需要手动来写shouldComponentUpdate了:
class CounterButton extends React.PureComponent { constructor(props) { super(props); this.state = {count: 1}; } render() { return ( ); } }
大多数情况下, 我们使用PureComponent能够简化我们的代码,并且提高性能,但是PureComponent的自动为我们添加的shouldComponentUpate函数,只是对props和state进行浅比较(shadow comparison),当props或者state本身是嵌套对象或数组等时,浅比较并不能得到预期的结果,这会导致实际的props和state发生了变化,但组件却没有更新的问题,例如下面代码有一个ListOfWords组件来将单词数组拼接成逗号分隔的句子,它有一个父组件WordAdder让你点击按钮为单词数组添加单词,但他并不能正常工作:
class ListOfWords extends React.PureComponent { render() { return{this.props.words.join(",")}; } } class WordAdder extends React.Component { constructor(props) { super(props); this.state = { words: ["marklar"] }; this.handleClick = this.handleClick.bind(this); } handleClick() { // 这个地方导致了bug const words = this.state.words; words.push("marklar"); this.setState({words: words}); } render() { return (); } }
这种情况下,PureComponent只会对this.props.words进行一次浅比较,虽然数组里面新增了元素,但是this.props.words与nextProps.words指向的仍是同一个数组,因此this.props.words !== nextProps.words 返回的便是flase,从而导致ListOfWords组件没有重新渲染,笔者之前就因为对此不太了解,而随意使用PureComponent,导致state发生变化,而视图就是不更新,调了好久找不到原因~。
最简单避免上述情况的方式,就是避免使用可变对象作为props和state,取而代之的是每次返回一个全新的对象,如下通过concat来返回新的数组:
handleClick() { this.setState(prevState => ({ words: prevState.words.concat(["marklar"]) })); }
你可以考虑使用Immutable.js来创建不可变对象,通过它来简化对象比较,提高性能。
这里还要提到的一点是虽然这里虽然使用了Pure这个词,但是PureComponent并不是纯的,因为对于纯的函数或组件应该是没有内部状态,对于stateless component更符合纯的定义,不了解纯函数的同学,可以参见这篇文章。
上面我们提到的创建组件的方式,都是用来创建包含状态和用户交互的复杂组件,当组件本身只是用来展示,所有数据都是通过props传入的时候,我们便可以使用Stateless Functional Component来快速创建组件。例如下面代码所示:
import React from "react"; const Button = ({ day, increment }) => { return () } Button.propTypes = { day: PropTypes.string.isRequired, increment: PropTypes.func.isRequired, }
这种组件,没有自身的状态,相同的props输入,必然会获得完全相同的组件展示。因为不需要关心组件的一些生命周期函数和渲染的钩子,所以不用继承自Component显得更简洁。
对比 createClass vs Component对于React.createClass 和 extends React.Component本质上都是用来创建组件,他们之间并没有绝对的好坏之分,只不过一个是ES5的语法,一个是ES6的语法支持,只不过createClass支持定义PureRenderMixin,这种写法官方已经不再推荐,而是建议使用PureComponent。
pureComponent vs Component通过上面对PureComponent和Component的介绍,你应该已经了解了二者的区别:PureComponent已经定义好了shouldUpdateComponent而Component需要显示定义。
Component vs Stateless Functional componentComponent包含内部state,而Stateless Functional Component所有数据都来自props,没有内部state;
Component 包含的一些生命周期函数,Stateless Functional Component都没有,因为Stateless Functional component没有shouldComponentUpdate,所以也无法控制组件的渲染,也即是说只要是收到新的props,Stateless Functional Component就会重新渲染。
Stateless Functional Component 不支持Refs
选哪个?这里仅列出一些参考:
createClass, 除非你确实对ES6的语法一窍不通,不然的话就不要再使用这种方式定义组件。
Stateless Functional Component, 对于不需要内部状态,且用不到生命周期函数的组件,我们可以使用这种方式定义组件,比如展示性的列表组件,可以将列表项定义为Stateless Functional Component。
PureComponent/Component,对于拥有内部state,使用生命周期的函数的组件,我们可以使用二者之一,但是大部分情况下,我更推荐使用PureComponent,因为它提供了更好的性能,同时强制你使用不可变的对象,保持良好的编程习惯。
参考文章optimizing-performance.html#shouldcomponentupdate-in-action
pureComponent介绍
react-functional-stateless-component-purecomponent-component-what-are-the-dif
4 different kinds of React component styles
react-without-es6
react-create-class-versus-component
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/81607.html
摘要:密码备份文件我们来看其测试运行结果密码备份文件这样做的好处之一就是,我们可以在这个方法中增加一些判断语句,起到数据安全的作用。 使用内部类的原因 学习总得知其所以然,前面的一篇文章中我有提到过关于java内部类的几种用法以及一些示例,但是不明白内部类具体可以做什么,显然学习起来很渺茫,今天的文章简单说一说使用内部类的几个原因,为了可读性更好,示例都极大的简化了,希望能给初学者一些帮助 ...
摘要:问题是处理完了,却也引发了自己的一些思考处理的异步操作,都有一些什么方法呢一回调函数传说中的就是来自回调函数。而回调函数也是最基础最常用的处理异步操作的办法。 引言 js的异步操作,已经是一个老生常谈的话题,关于这个话题的文章随便google一下都可以看到一大堆。那么为什么我还要写这篇东西呢?在最近的工作中,为了编写一套相对比较复杂的插件,需要处理各种各样的异步操作。但是为了体积和兼容...
摘要:还记得刚开始学习的时候,内存管理前端掘金作为一门高级语言,并不像低级语言那样拥有对内存的完全掌控。第三方库的行代码内实现一个前端掘金前言本文会教你如何在行代码内,不依赖任何第三方的库,用纯实现一个。 (译) 如何使用 JavaScript 构建响应式引擎 —— Part 1:可观察的对象 - 掘金原文地址:How to build a reactive engine in JavaSc...
摘要:还记得刚开始学习的时候,内存管理前端掘金作为一门高级语言,并不像低级语言那样拥有对内存的完全掌控。第三方库的行代码内实现一个前端掘金前言本文会教你如何在行代码内,不依赖任何第三方的库,用纯实现一个。 (译) 如何使用 JavaScript 构建响应式引擎 —— Part 1:可观察的对象 - 掘金原文地址:How to build a reactive engine in JavaSc...
摘要:下面,我就从基本对象说起,聊一聊不可变数据和的一切。可变和共享是万恶之源不可变数据其实是函数式编程相关的重要概念。相对的,函数式编程中认为可变性是万恶之源。针对于此,我推荐一款已经大名鼎鼎的类库来处理不可变数据。 作为前端开发者,你会感受到JS中对象(Object)这个概念的强大。我们说JS中一切皆对象。最核心的特性,例如从String,到数组,再到浏览器的APIs,对象这个概念无处不...
阅读 1418·2021-09-02 09:53
阅读 2678·2021-07-29 13:50
阅读 1726·2019-08-30 11:07
阅读 1584·2019-08-30 11:00
阅读 1461·2019-08-29 14:00
阅读 1856·2019-08-29 12:52
阅读 2575·2019-08-29 11:11
阅读 3431·2019-08-26 12:23