React 之快速深入 stateless component(无状态组件) 和 class component (类组件)

2,437 阅读2分钟

本文前提:

  1. 假设你已经大概了解过state和props是如何影响渲染的了
  2. 你了解shouldComponentUpdate、componentwillreceiveprops、purecomponent是如何影响渲染

两者什么区别

写法

无状态组件
const MyStatelessComponent = props => <div>{props.name}</div>;
类组件
class MyComponentClass extends React.Component {
  render() {
    return <div>{this.props.name}</div>;
  }
}

状态和生命周期

无状态组件
  • 优势是打包后体积小,写法清爽,和理论上的性能出色,这部分下面会详细说明
  • 劣势是无状态(state)和生命周期,无法利用生命周期减少render,也就是说只要props发生变化就会重新渲染(这块有解决办法,下面会说到)
// 打包后如下
var MyStatelessComponent = function MyStatelessComponent(props){ 
  return React.createElement(
    “div”,
    null,
    props.name 
  ); 
}
类组件
  • 优势是有状态(state)和生命周期
  • 劣势是体积大和理论上的性能差
// 打包后如下
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var MyComponentClass = function (_React$Component) {
  _inherits(MyComponentClass, _React$Component);
function MyComponentClass() {
    _classCallCheck(this, MyComponentClass);
return _possibleConstructorReturn(this, (MyComponentClass.__proto__ || Object.getPrototypeOf(MyComponentClass)).apply(this, arguments));
  }
_createClass(MyComponentClass, [{
    key: "render",
    value: function render() {
      return React.createElement(
        "div",
        null,
        this.props.name
      );
    }
  }]);
return MyComponentClass;
}(React.Component);

打包后实际体积约 1.2kb对97字节,差别很大

性能差别

渲染10000个无状态组件和类组件,几乎无差别,或者说差别小以至于在生产环境中无需考虑这点.

减少render次数、性能优化

类组件

shouldComponentUpdate、componentwillreceiveprops、React.PureComponent都会减少渲染次数,使用细节不在此赘述,建议纯api用法层面的东西还是不要通过二手知识获取,而是去刷官网文档

无状态组件

React.memo api,无状态组件版的React.PureComponent

**注意:**浅比较只能直接比较原始类型,父组件传递的props是对象时,每次更新(不论值变不变)都是返回一个新对象,(哪怕值没变),新旧对象不同,引用不用,所以浅比较失败。

<Bar data={{a: 1, b: 2}} />

class Bar extends React.PureComponent {
  render() {
    return <div>{this.props.data.a}</div>;
  }
}
// 虽然使用了PureComponent,但父组件每次render都会触发 子组件重新render

更好性能的做法是

<Bar firstName="John" lastName="Schilling" />