Unterschiede: 1. Eine Funktionskomponente ist eine reine Funktion, die ein Requisitenobjekt empfängt und ein Reaktionselement zurückgibt, während eine Klassenkomponente React.Component erben und eine Renderfunktion erstellen muss, um ein Reaktionselement zurückzugeben. 2. Funktionskomponenten haben keinen Lebenszyklus und Status, Klassenkomponenten jedoch schon.
,
Die Betriebsumgebung dieses Artikels: Windows 7-System, Dell G3-Computer, Reaktionsversion 17.0.1.
In diesem Artikel zeige ich Ihnen, was der Unterschied zwischen Funktionskomponenten und Klassenkomponenten ist und wie Sie beim Codieren wählen sollten.
Der einfachste Weg, eine Komponente zu definieren, ist die Verwendung der Funktion JavaScript
: JavaScript
函数:
import React from 'react' const Welcome = (props) => { return <h1>welcome, {props.name}</h1> } export default Welcome
这个函数接收一个props
对象并返回一个react
元素
你也可以使用ES6 class
语法去写一个组件:
import React from 'react' class Welcome extends React.Component { constructor(props) { super(props) } render() { return <h1>welcome, {this.props.name}</h1> } } export default Welcome
这两个版本是等价的,它们具有相同的输出。那么我们应该去选择哪一种实现方式呢?下面我们来对他们进行比较
1、语法上
两者最明显的不同就是在语法上,函数组件是一个纯函数,它接收一个props
对象返回一个react
元素。而类组件需要去继承React.Component
并且创建render
函数返回react
元素,这将会要更多的代码,虽然它们实现的效果相同。
我们更深入的了解下,使用babel7
分别对他们进行转译
函数组件转译结果:
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; var _react = _interopRequireDefault(require("react")); var Welcome = function Welcome(props) { return _react["default"].createElement("h1", null, "welcome, ", props.name); }; var _default = Welcome; exports["default"] = _default;
类组件转译结果:
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = void 0; var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _react = _interopRequireDefault(require("react")); var Welcome = /*#__PURE__*/ function (_React$Component) { (0, _inherits2["default"])(Welcome, _React$Component); function Welcome(props) { (0, _classCallCheck2["default"])(this, Welcome); return (0, _possibleConstructorReturn2["default"])(this, (0, _getPrototypeOf2["default"])(Welcome).call(this, props)); } (0, _createClass2["default"])(Welcome, [{ key: "render", value: function render() { return _react["default"].createElement("h1", null, "welcome, ", this.props.name); } }]); return Welcome; }(_react["default"].Component); var _default = Welcome; exports["default"] = _default;
可以看到类组件转译成ES5
后代码更多更长,但这不是区分它们的主要因素,仅仅了解一下。
2、状态管理
因为函数组件是一个纯函数,你不能在组件中使用setState()
,这也是为什么把函数组件称作为无状态组件。
如果你需要在你的组件中使用state
,你可以选择创建一个类组件或者将state
提升到你的父组件中,然后通过props
对象传递到子组件。
3、生命周期钩子
你不能在函数组件中使用生命周期钩子,原因和不能使用state
一样,所有的生命周期钩子都来自于继承的React.Component
中。
因此,如果你想使用生命周期钩子,那么需要使用类组件。
注意:在react16.8
版本中添加了hooks
,使得我们可以在函数组件中使用useState
钩子去管理state
,使用useEffect
钩子去使用生命周期函数。因此,2、3两点就不是它们的区别点。从这个改版中我们可以看出作者更加看重函数组件,而且react
团队曾提及到在react
之后的版本将会对函数组件的性能方面进行提升。
4、调用方式
如果SayHi
是一个函数,React
需要调用它:
// 你的代码 function SayHi() { return <p>Hello, React</p> } // React内部 const result = SayHi(props) // » <p>Hello, React</p>
如果SayHi
是一个类,React
需要先用new
操作符将其实例化,然后调用刚才生成实例的render
方法:
// 你的代码 class SayHi extends React.Component { render() { return <p>Hello, React</p> } } // React内部 const instance = new SayHi(props) // » SayHi {} const result = instance.render() // » <p>Hello, React</p>
可想而知,函数组件重新渲染将重新调用组件方法返回新的react
元素,类组件重新渲染将new
一个新的组件实例,然后调用render
类方法返回react
元素,这也说明为什么类组件中this
是可变的
5、获取渲染时的值
这一点是他们最大差异,但又常常被人们忽略。
考虑以下组件:
function ProfilePage(props) { const showMessage = () => { alert('Followed ' + props.user); } const handleClick = () => { setTimeout(showMessage, 3000); } return ( <button>Follow</button> ) }
UserProfile
组件很简单,就一个Follow
按钮,该按钮使用了setTimeout
模拟网络请求。用户点击这个按钮之后会弹出一个警告框。如果props.user
为'Dan'
,它将在三秒钟后显示'Followed Dan'
。
我们如何将其编写为类?天真的翻译可能像这样:
class ProfilePage extends React.Component { showMessage() { alert('Followed ' + this.props.user); } handleClick() { setTimeout(this.showMessage.bind(this), 3000); } render() { return <button>Follow</button> } }
通常认为这两个代码段是等效的。人们经常在这些模式之间自由重构,而没有注意到它们的含义
但是,这两个代码段是完全不同的。好好看看他们。你看到区别了吗?
分别按下面的顺序来操作Follow
按钮:
Follow
按钮3s
showMessage() { alert('Followed ' + this.props.user); }
props
-Objekt und gibt ein react
zurück elementES6-Klasse
-Syntax verwenden, um eine Komponente zu schreiben: class ProfilePage extends React.Component { showMessage(user) { alert('Followed ' + user); } handleClick() { cosnt {user} = this.props setTimeout(this.showMessage.bind(this, user), 3000); } render() { return <button>Follow</button> } }
Die beiden Versionen sind gleichwertig, sie haben die gleiche Ausgabe. Welche Implementierungsmethode sollten wir also wählen? Vergleichen wir sie unten
🎜1. Grammatik🎜🎜Der offensichtlichste Unterschied zwischen den beiden liegt in der Grammatik: Eine Funktionskomponente ist eine reine Funktion, die einprops
-Objekt empfängt und ein react
-Element zurückgibt. Klassenkomponenten müssen React.Component
erben und eine render
-Funktion erstellen, um ein react
-Element zurückzugeben, was mehr Code erfordert, obwohl sie implementiert sind Der Effekt ist der gleiche. 🎜🎜Lassen Sie uns einen genaueren Blick darauf werfen und babel7
verwenden, um sie separat zu übersetzenclass ProfilePage extends React.Component { render() { // 获取props cosnt props = this.props // 它们不是类方法 const showMessage = () => { alert('Followed ' + props.user); } const handleClick = () => { setTimeout(showMessage, 3000) } return <button>Follow</button> } }
ES5
, der Code wird immer länger, aber das ist nicht der Hauptfaktor, der sie auszeichnet, man muss es nur verstehen. 🎜🎜2. Zustandsverwaltung🎜🎜Da es sich bei der Funktionskomponente um eine reine Funktion handelt, können Sie setState nicht im verwenden Komponente ()
, weshalb Funktionskomponenten als zustandslose Komponenten bezeichnet werden. 🎜🎜Wenn Sie state
in Ihrer Komponente verwenden müssen, können Sie eine Klassenkomponente erstellen oder state
zu Ihrer übergeordneten Komponente hochstufen und dann props-Objekte werden an untergeordnete Komponenten übergeben. 🎜🎜<strong><span style="font-size: 18px;">3. Lebenszyklus-Hooks</span></strong>🎜🎜Sie können keine Lebenszyklus-Hooks in Funktionskomponenten verwenden und die Gründe, warum Sie sie nicht verwenden können <code>State
ist derselbe, alle Lebenszyklus-Hooks stammen von der geerbten React.Component
. 🎜🎜Wenn Sie also Lebenszyklus-Hooks verwenden möchten, müssen Sie Klassenkomponenten verwenden. 🎜🎜Hinweis: Hooks
wurde in der react16.8
-Version hinzugefügt, sodass wir den useState
-Hook zum Verwalten von Funktionen verwenden können Komponenten state
verwenden Sie den Hook useEffect
, um Lebenszyklusfunktionen zu verwenden. Daher sind die Punkte 2 und 3 nicht ihre Unterschiede. Aus dieser Überarbeitung können wir ersehen, dass der Autor den Funktionskomponenten mehr Aufmerksamkeit schenkt, und das react
-Team hat erwähnt, dass die Leistung der Funktionskomponenten in nachfolgenden Versionen von react
verbessert wird > . 🎜🎜4. Aufrufmethode🎜🎜Wenn SayHi
eine Funktion ist, React muss es aufrufen: 🎜rrreee🎜Wenn SayHi
eine Klasse ist, muss React
zuerst den Operator new
verwenden zu instanziieren, und rufen Sie dann die render
-Methode auf, die gerade die Instanz generiert hat: 🎜rrreee🎜Wie Sie sich vorstellen können, wird beim erneuten Rendern der Funktionskomponente die Komponentenmethode erneut aufgerufen, um die neue zurückzugeben react
-Element, Klassenkomponente rendert new
eine neue Komponenteninstanz erneut und ruft dann die Klassenmethode render
auf, um react-Element, das auch erklärt, warum Klassenkomponenten <code>this
variabel sind 🎜🎜5. Rufen Sie den Wert beim Rendern ab 🎜🎜Das ist ihr größter Unterschied, aber er wird von den Menschen oft ignoriert. 🎜🎜Berücksichtigen Sie die folgenden Komponenten: 🎜rrreee🎜 Die Komponente UserProfile
ist sehr einfach, nur eine Schaltfläche Folgen
, die setTimeout
verwendet, um ein Netzwerk zu simulieren Anfrage. Nachdem der Benutzer auf diese Schaltfläche geklickt hat, wird ein Warnfeld angezeigt. Wenn props.user
'Dan'
ist, wird nach drei Sekunden 'Followed Dan'
angezeigt. 🎜🎜Wie schreiben wir das als Klasse? Eine naive Übersetzung könnte so aussehen: 🎜rrreee🎜 Es ist üblich zu glauben, dass diese beiden Codeschnipsel gleichwertig sind. Oft wird zwischen diesen Mustern frei umgestaltet, ohne deren Implikationen zu bemerken 🎜🎜Diese beiden Codeschnipsel sind jedoch völlig unterschiedlich. Schauen Sie sie sich genau an. Sehen Sie den Unterschied? 🎜🎜 Betätigen Sie die Schaltfläche Folgen
in der folgenden Reihenfolge: 🎜Folgen
🎜🎜Ändern Sie die Dropdown-Auswahl vor 3s Code> Elementoptionen🎜🎜Lesen Sie den Inhalt des Popup-Warnfelds🎜🎜🎜Sie werden feststellen, dass es einen Unterschied zwischen Funktionskomponenten und Klassenkomponenten gibt:🎜<p><strong>函数组件</strong>:按上面所列的三个步骤操作时,当用户在<code>3s
前更改下拉选择框的选项时,h1
的用户名会立马改变,而3s
后弹出的警告框中的用户名并不会改变
类组件:按上面所列的三个步骤操作时,当用户在3s
前更改下拉选择框的选项时,h1
中的用户名会立马改变,而3s
后弹出的警告框中的用户名也会改变
那么,为什么我们的类示例会这样表现呢?
让我们仔细看一下showMessage
类中的方法:
showMessage() { alert('Followed ' + this.props.user); }
在showMessage
方法中读取了this.props.user
(也是我们要输出的用户名称)。而React
中的props
是不可变的,但是this
是可变的,而且是一直是可变的。这也是类组件中this
的目的。React
自身会随着时间的推移对this
进行修改,以便你可以在render
函数或生命周期中读取新的版本。
因此,如果组件在请求重新渲染时,this.props
将会改变。showMessage
方法会从新的props
中读取user
。你所看到的效果也正是因为这个原因。
在React
中的组件,UI
在概念上可以理解是程序当前状态的函数,那么事件处理就是让UI
的渲染结果一部分一部分可视化输出。我们的事件处理程序属于具有特定props
和state
的特定渲染。但是,当回调超时的话,this.props
就会打破这种联系。示例中的showMessage
方法在回调时没有绑定到任何特定的渲染,因此它会丢失真正的props
。
那么我们有没有一种较好的方式可以使用正确的props
来修复render
和showMessage
回调之间的联系。我们可以在事件发生的早期,将this.props
传递给超时完成的处理程序来尝试着解决这个问题。这种解决方式属于闭包的范畴。
class ProfilePage extends React.Component { showMessage(user) { alert('Followed ' + user); } handleClick() { cosnt {user} = this.props setTimeout(this.showMessage.bind(this, user), 3000); } render() { return <button>Follow</button> } }
我们使用闭包机制将上一状态的值保存下来待showMessage
方法调用。即使this.props
发生变化,但并不改变user
这种方法虽然解决我们前面所提到的问题,但是这种方法代码会随着props
的个数增加,代码也会变得更加冗余也易于出错。如果我们也需要访问state
。如果showMessage
调用另一个方法,该方法会读取this.props.something
或this.state.something
。我们又会碰到同样的问题。所以我们必须通过this.props
作为showMessage
的参数来修复它们之间存在的问题。
但这么做会破坏类提供的特性。也令人难于记住或执行。另外,在handleClick
中内联alert
中的代码并不能解决更大的问题。我们希望以一种允许代码分解成更多方法的方式来构造代码,同时还可以读取与其相关的render
所对应的props
和state
。
或许,我们可以在类的构造函数中绑定这些方法:
class ProfilePage extends React.Component { render() { // 获取props cosnt props = this.props // 它们不是类方法 const showMessage = () => { alert('Followed ' + props.user); } const handleClick = () => { setTimeout(showMessage, 3000) } return <button>Follow</button> } }
这样一来,函数组件和类组件所达到的效果都一样了。在类组件中可以捕获渲染时的props
。效果上看上去是一样了,但看起来怪怪的。如果在类组件中的render
中定义函数而不是使用类方法,那么还有使用类的必要性?
更多编程相关知识,请访问:编程课程!!
Das obige ist der detaillierte Inhalt vonWas ist der Unterschied zwischen Funktionskomponenten und Klassenkomponenten in React?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!