React中PureComponent 【www.129028.com金沙】和 Component区别

日期:2020-05-07编辑作者:Web前端

以上就是通过实际开发场景来分享 PureComponent 与 Component 的不同之处。

7. 高阶组件

高阶组件(higher-order-components)是react中对组件逻辑进行重用的高级技术。但高阶组件本身并不是React API。它只是一种模式,这种模式是由react自身的组合性质必然产生的。

这里需要明确一点:高阶组件并不是一个组件类,它是一个函数,接收一个组件并返回一个新组件。

const newComponent = higherOrderComponent(oldComponent);

高阶组件(HOC)是一种修饰者模式,它对原有组件进行改造并生成新的组件,对组件代码进行的复用。

下面例子�来自 https://discountry.github.io/react/docs/higher-order-components.html;

假设有一个 CommentList 组件:

class CommentList extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {
      // "DataSource" is some global data source
      comments: DataSource.getComments()
    };
  }
  componentDidMount() {
    // Subscribe to changes
    DataSource.addChangeListener(this.handleChange);
  }
  componentWillUnmount() {
    // Clean up listener
    DataSource.removeChangeListener(this.handleChange);
  }
  handleChange() {
    // Update component state whenever the data source changes
    this.setState({
      comments: DataSource.getComments()
    });
  }
  render() {
    return (
      <div>
        {this.state.comments.map((comment) => (
          <Comment comment={comment} key={comment.id} />
        ))}
      </div>
    );
  }
}

之后你又有一个 BlogPost 组件:

class BlogPost extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {
      blogPost: DataSource.getBlogPost(props.id)
    };
  }
  componentDidMount() {
    DataSource.addChangeListener(this.handleChange);
  }
  componentWillUnmount() {
    DataSource.removeChangeListener(this.handleChange);
  }
  handleChange() {
    this.setState({
      blogPost: DataSource.getBlogPost(this.props.id)
    });
  }
  render() {
    return <TextBlock text={this.state.blogPost} />;
  }
}

CommentList 和 BlogPost 组件并不相同 —— 他们调用了 DataSource 的不同方法获取数据,并且他们渲染的输出结果也不相同。但是,他们的大部分实现逻辑是一样的:

  1. 挂载组件时候监听数据变化;
  2. 数据变化是改变组件状态;
  3. 组件卸载时移除监听函数;

设想一下,在一个大型的应用中,这种从 DataSource 订阅数据并调用 setState 的模式将会一次又一次的发生。我们就可以抽象出一个模式,该模式允许我们在一个地方定义逻辑并且能对所有的组件使用,这就是高阶组件的精华所在。

我们写一个函数,该函数能够创建类似 CommonList 和 BlogPost 从 DataSource 数据源订阅数据的组件 。该函数接受一个子组件作为其中的一个参数,并从数据源订阅数据作为props属性传入子组件。我们把这个函数取个名字 withSubscription:

const CommentListWithSubscription = withSubscription(
  CommentList,
  (DataSource) => DataSource.getComments()
);
const BlogPostWithSubscription = withSubscription(
  BlogPost,
  (DataSource, props) => DataSource.getBlogPost(props.id)
});

第一个参数是包裹组件(wrapped component),第二个参数会从 DataSource和当前props 属性中检索应用需要的数据。
当 CommentListWithSubscription 和 BlogPostWithSubscription 渲染时, 会向CommentList 和 BlogPost 传递一个 data props属性,该 data属性的数据包含了从 DataSource 检索的最新数据:

// This function takes a component...
function withSubscription(WrappedComponent, selectData) {
  // ...and returns another component...
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.handleChange = this.handleChange.bind(this);
      this.state = {
        data: selectData(DataSource, props)
      };
    }
    componentDidMount() {
      // ... that takes care of the subscription...
      DataSource.addChangeListener(this.handleChange);
    }
    componentWillUnmount() {
      DataSource.removeChangeListener(this.handleChange);
    }
    handleChange() {
      this.setState({
        data: selectData(DataSource, this.props)
      });
    }
    render() {
      // ... and renders the wrapped component with the fresh data!
      // Notice that we pass through any additional props
      return <WrappedComponent data={this.state.data} {...this.props} />;
    }
  };
}

因为 withSubscription 就是一个普通函数,你可以添加任意数量的参数。例如,你或许会想使 data 属性可配置化,使高阶组件和包裹组件进一步隔离开。或者你想要接收一个参数用于配置 shouldComponentUpdate 函数,或配置数据源的参数。这些都可以实现,因为高阶组件可以完全控制新组件的定义。

在很多第三方库中都使用到了高阶组件,比如 react-router 中的 connect 就是连接了 redux 状态与组件的高阶组件,或者 react-router 中的 withRouter 用来给组件注入 �history 数据。

参考

来自阿诚的日志

import React, { Component } from 'react'export default class TutTitle extends Component { render() { console.log('rendering tut title ...'); return ( div span{this.props.name}/span /div ) }}

import React, { Component } from 'react'export default class TutSubtitle extends Component { render() { console.log('rendering sub tittle...') return ( div span{this.props.name}/span /div ) }}

1. 前言

在 React 中,一切皆是组件,因此理解组件的工作流与核心尤为重要。
我们有多种创建组件的方式(不仅 Component),很多时候选择使用哪种组件的创建方式是值得深入考究的;同时对于 React 中有太多的组件概念,无状态组件、高阶组件… 常常也是让新手一头雾水,因此本文也尝试解释分析不同的组件概念。

当然可以使用 PureComponent 类型来创建 Subtitle 组件,因为 PureComponent 的特殊性我们就无需做额外工作也能够达到预期的效果。

6.2 Dumb 组件

Dumb 组件又称为 木偶组件,它负责展示作用以及响应用户交互,它一般是无状态的(在如 Modal 等类组件中可能会维护少量自身状态);
一般而言 Dumb 组件会拆分为一个个可复用、功能单一的组件;
因此 Dumb 组件使用函数式组件定义,当其需要对重渲染进行优化时则可以使用 PureComponent。

所以 Smart 组件更多关注与数据以及业务逻辑,而 Dumb 组件与数据和业务解耦,主要复杂 UI 层面的展示与交互。

也可以更精确地进行控制通过对比nextProps.name !== this.props.name;来实现。

2. 组件的创建方式

我们可以通过 shouldComponentUpdate 来阻止组件跟随其父级组件 state 的变化而更新。如果在 shouldComponentUpdate 返回 true 时,只要父级组件 state 更新其就随之更新。

2.2 PureComponent

首先我们来理解下 React 组件执行重渲染(re-render)更新的时机,一般当一个组件的 props (属性)或者 state (状态)发生改变的时候,也就是父组件传递进来的 props 发生变化或者使用 this.setState函数时,组件会进行重新渲染(re-render);
而在接受到新的 props 或者 state 到组件更新之间会执行其生命周期中的一个函数 shouldComponentUpdate,当该函数返回 true 时才会进行重渲染,如果返回false 则不会进行重渲染,在这里 shouldComponentUpdate 默认返回 true
因此当组件遇到性能瓶颈的时候可以在 shouldComponentUpdate 中进行逻辑判断,来自定义组件是否需要重渲染。

PureComponent 是在 react v15.3.0 中新加的一个组件,从 React 源码中可以看到它是继承了 Component 组件:

/**
 * Base class helpers for the updating state of a component.
 */
function ReactPureComponent(props, context, updater) {
  // Duplicated from ReactComponent.
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  // We initialize the default updater but the real one gets injected by the
  // renderer.
  this.updater = updater || ReactNoopUpdateQueue;
}
function ComponentDummy() {}
ComponentDummy.prototype = ReactComponent.prototype;
var pureComponentPrototype = (ReactPureComponent.prototype = new ComponentDummy());
pureComponentPrototype.constructor = ReactPureComponent;
// Avoid an extra prototype jump for these methods.
Object.assign(pureComponentPrototype, ReactComponent.prototype);
pureComponentPrototype.isPureReactComponent = true;

同时在shouldComponentUpdate函数中有一段这样的逻辑:

if (type.prototype && type.prototype.isPureReactComponent) {
  return (
    !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
  );
}

因此 PureReactComponent 组件和 ReactComponent 组件的区别就是它在 shouldComponentUpdate 中会默认判断新旧属性和状态是否相等,如果没有改变则返回 false,因此它得以减少组件的重渲染。
当然,这里对新旧属性和状态的比较都为类的浅比较。

优点:

  1. 在 shouldComponentUpdate 生命周期做了优化会自动 shadow diff 组件的 state 和 props,结合 immutable 数据就可以很好地去做更新判断;
  2. 隔离了父组件与子组件的状态变化;

缺点:

  1. shouldComponentUpdate 中的 shadow diff 同样消耗性能;
  2. 需要确保组件渲染仅取决于 props 与 state ;

下面创建了个组件被别是 TutTitle 和 TutSubTitle

6.1 Smart 组件

Smart 组件又称为 容器组件,它负责处理应用的数据以及业务逻辑,�同时将状态数据与操作函数作为属性传递给子组件;
一般而言它仅维护很少的 DOM,其所有的 DOM 也仅是作为布局等作用。

完整代码如下

2.1 Component

这是 React 中最常见与最通用的组件创建方式:

class Container extends React.Component {
  construcor (props) {
    super(props);
    this.state = {};
  }
  render () {
    return (
      <div className="container">{ this.props.children }</div>
    );
  }
}

使用了 es6 中类的继承方法,当然它也有 es5 的写法(createClass):

var Container = React.createClass({
  getInitialState: function() {
    return {};
  },
  render () {
    return (
      <div className="container">{ this.props.children }</div>
    );
  }
});

两种方法都是一样的返回一个 Container 的组件类,这是我们通常创建组件的方式,因此不做更多阐述了。

时间: 2019-09-07阅读: 143标签: Component

6. Smart 组件与 Dumb 组件

当应用的视图层与数据层解耦的情况下,比如结合 Redux 或者 Mobx 等状态管理库,那么在一个应用中组件又分为 Smart 组件与 Dumb 组件。

 shouldComponentUpdate(nextProps, nextState, nextContext) { return nextProps.name !== this.props.name; }

2.3 函数式组件

在 React 中还可以以函数来定义一个组件,称之为函数式组件:

const Button = ({ children, ...props }) => (
  <button {...props}>{children}</button>
);

函数式组件又称为无状态(stateless)组件,它不存在自身的状态,并且没有普通组件中的各种生命周期方法,同时其函数式的写法决定了其渲染只由属性决定;

优点:

  1. 简化代码、专注于 render;
  2. 组件不需要被实例化,无生命周期,提升性能;
  3. 输出(渲染)只取决于输入(属性),无副作用;
  4. 视图和数据的解耦分离;

缺点:

  1. 无法使用 ref;
  2. 无生命周期方法;
  3. 无法控制组件的重渲染,因为无法使用 shouldComponentUpdate 方法,当组件接受到新的属性时则会重渲染;

  4. Component 与 PureComponent 的选择


先看一个例子:

class UserAvatar extends React.Component {
  render() {
    console.log('UserAvatar re-render');
    return (
      <div>
        < img src={this.props.imageUrl} />
      </div>
    );
  }
}
class Container extends React.Component {
  construcor (props) {
    super(props);
    this.state = {
      name: '',
      avatar: "//xxx.xxx/xxxx",
    };
  }
  render () {
    const { name, avatar } = this.state;
    console.log('Container re-render');
    return (
      <div className="container">
        <UserAvatar imageUrl={} />
        <div>{name}</div>
        <button onClick={() => this.setState({ name: 'n' })}>CLICK</button>
      </div>
    );
  }
}

例子中 Container 组件为 UserAvatar 组件的父组件,当 Container 组件的 state 中的 name 发生改变的时候,Container 执行重渲染,而 UserAvatar 也执行了重渲染,即使它的属性没有发生改变;
虽然在 React 中实现的是 diff 比较,实际的 dom 并不一定会被更新,但是 diff 的比较也是非常消耗性能的;
当把 UserAvatar 改成继承 PureComponent 之后,那么它将会在 shouldComponentUpdate 进行浅比较,如果属性没有改变,则不会进行重渲染。

因此相比于 Component ,PureComponent 有性能上的更大提升:

  1. 减少了组件无意义的重渲染(当 state 和 props 没有发生变化时),当结合 immutable 数据时其优更为明显;
  2. 隔离了父组件与子组件的状态变化;

当我们开始使用 PureComponent 组件,并不需要做更多的事情,所做的仅仅是将 Component 替换成 PureComponent;

那么既然 PureComponent 相比 Component 对性能和渲染上做了更多的优化处理,那么我们是否应该在所有地方都使用 PureComponent 替换 Component 吗?
答案当然是否定的,如果是这样那么 React 官方早应该使用 PureComponent 作为其默认组件了。
原因如下:

  1. 我们应该避免过早优化,当在应用出现性能瓶颈的时候才需要去排查与解决这部分的渲染;
  2. 在 shouldComponentUpdate 中先置进行新旧属性与状态的浅比较同样是对于性能上的消耗,而其带来的优化效果与性能消耗还需结合实际情况进行抉择;
  3. PureComponent 在 shouldComponentUpdate 所做的也仅是浅层的对象比较,在属性/状态层级结构较深较复杂的情况下容易出现深层 bug,当然如果引入了 immutable 数据那么这里的风险将会大大减小;
  4. 在我们真正遇到性能瓶颈时,很多时候的处理并不仅仅是比较属性/状态是否改变,因此 PureComponent 在这种情况下优势也不大。

  5. 选择函数式组件


那么何时使用函数式组件呢?

  1. 对于函数式组件,由于它不需要处理复杂的生命周期函数,因此它在性能上也有一定优势。
  2. 当一个展示性组件的渲染仅仅依赖于其属性,使用函数式组件可以保证它的“纯”,并且对组件本身无任何副作用;
  3. 由于它无法控制它的重渲染(无 shouldComponentUpdate 生命周期),因此我们希望该组件的属性数据相对较少,同时组件本身结构相对简单;
  4. 函数式组件能够更好地与业务抽离,并且易于测试。
    因此对于简单的通用性组件,比如自定义的 <Button />、<Input /> 等组件选择函数式组件是再好不过的了。

  5. www.129028.com金沙,纯组件与无状态组件


那么什么样的组件才算是 “纯” 的呢?
React 中将 PureComponent 定义为纯组件,假如一个组件只和 props 和 state 有关系,给定相同的 props 和 state 就会渲染出相同的结果,且不受副作用影响,那么这个组件就叫做纯组件,或者说纯组件只依赖于组件的 props 和 state 。
那么使用 PureComponent 的组件就一定是所谓的 “纯” 组件了吗?
事实上,在 PureComponent 中仍然会在生命周期中对其产生副作用,比如你可以在 componentDidMount 发送 ajax 请求,或者通过计算 dom 去改变某个 div 的高度。
因此组件的 “纯” 取决于你实际代码中是如何实现的。

而对于无状态组件它仅由其属性决定,且它通过函数式定义因此不存在副作用,无状态组件也是一种 “纯” 组件。

本文由www.129028.com金沙发布于Web前端,转载请注明出处:React中PureComponent 【www.129028.com金沙】和 Component区别

关键词:

nginx负载均衡如何实现www.129028.com金沙?

什么是nginx? Nginx是一个免费的,开源的,高性能的服务器和反向代理服务器软件,同时它也可以为IMAP和POP3服务器代...

详细>>

最少编码原则

这的确是大多数程序员,甚至是那些高级程序员都很容易混淆的一个重点。作为一名程序员,编写代码无疑是你职业...

详细>>

CSS中cursor 鼠标指针光标样式

值 前面url()是自定义鼠标的样式,图像的地址,后面的参数是 css 标准的 cursor样式,(IE下面可以不需要) 出现版本...

详细>>

AJAX:如何处理书签和后退按钮(1)

或者如果你不喜欢onclick: window.onload = initialize;function initialize() { // initialize the DHTML History // framework dhtmlHistory.initial...

详细>>