首頁 >web前端 >js教程 >React中函數元件與類別元件的差別是什麼?

React中函數元件與類別元件的差別是什麼?

青灯夜游
青灯夜游原創
2020-12-03 09:58:1621042瀏覽

區別:1、函數元件是純函數,它接收一個props物件回傳一個react元素;而類別元件需要去繼承React.Component並且建立render函數回傳react元素。 2.函數組件沒有生命週期和狀態state,而類別組件有。

React中函數元件與類別元件的差別是什麼?

本文操作環境:windows7系統、Dell G3電腦、react17.0.1版。

在本文中我將向你展示函數元件和類別元件有什麼不同,並且在編碼過程中應該如何選擇?

定義一個元件最簡單的方式就是使用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按鈕:

  1. #先點選Follow按鈕
  2. 3s先前更改下拉選擇項目的選項
  3. 閱讀彈出的警告框內容

你會發現函數元件和類別元件是有區別的:

函数组件:按上面所列的三个步骤操作时,当用户在3s前更改下拉选择框的选项时,h1的用户名会立马改变,而3s后弹出的警告框中的用户名并不会改变

类组件:按上面所列的三个步骤操作时,当用户在3s前更改下拉选择框的选项时,h1中的用户名会立马改变,而3s后弹出的警告框中的用户名也会改变

React中函數元件與類別元件的差別是什麼?

React中函數元件與類別元件的差別是什麼?

那么,为什么我们的类示例会这样表现呢?

让我们仔细看一下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的渲染结果一部分一部分可视化输出。我们的事件处理程序属于具有特定propsstate的特定渲染。但是,当回调超时的话,this.props就会打破这种联系。示例中的showMessage方法在回调时没有绑定到任何特定的渲染,因此它会丢失真正的props

那么我们有没有一种较好的方式可以使用正确的props来修复rendershowMessage回调之间的联系。我们可以在事件发生的早期,将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.somethingthis.state.something。我们又会碰到同样的问题。所以我们必须通过this.props作为showMessage的参数来修复它们之间存在的问题。

但这么做会破坏类提供的特性。也令人难于记住或执行。另外,在handleClick中内联alert中的代码并不能解决更大的问题。我们希望以一种允许代码分解成更多方法的方式来构造代码,同时还可以读取与其相关的render所对应的propsstate

或许,我们可以在类的构造函数中绑定这些方法:

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中定义函数而不是使用类方法,那么还有使用类的必要性?

更多编程相关知识,请访问:编程课程!!

以上是React中函數元件與類別元件的差別是什麼?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn