前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >React Native之PureComponent

React Native之PureComponent

作者头像
VV木公子
发布2020-02-18 17:14:50
7.6K0
发布2020-02-18 17:14:50
举报
文章被收录于专栏:TechBox

前言

PureComponent继承自Component。PureComponent几乎和Component完全相同。

唯一不同之处在于Component的shouldComponentUpdate的默认实现是返回true。而PureComponent通过props和state的浅比较实现shouldComponentUpdate,某些情况下使用PureComponent可以减少不必要的渲染,提升性能。

PureComponent原理

之所以,Components的props和state每次发生变化时都会触发render。是因为Component的shouldComponentUpdate方法默认返回了true。而PureComponent的shouldComponentUpdate并非简单的返回了true。

当PureComponent组件更新时,如果组件的 props 和 state 都没发生改变, render 方法就不会触发,省去 Virtual DOM 的生成和比对过程,达到提升性能的目的。具体就是 React 自动帮我们做了一层浅比较shallowEqual(即指针比较),如下是PureComponent的shouldComponentUpdate的源码:

代码语言:javascript
复制
if (this._compositeType === CompositeTypes.PureClass) {
  shouldUpdate = !shallowEqual(prevProps, nextProps)
  || !shallowEqual(inst.state, nextState);
}

关于浅比较的更多内容可以参考https://www.imweb.io/topic/598973c2c72aa8db35d2e291

当把之前和下一个的props和state作比较,浅比较将检查原始值是否有相同的值(例如:1 == 1或者ture==true);而对于数组和对象类型,将会比较引用是否相同。

PureComponent注意事项

1.可变数据变化前后不能使用同一个引用。 2.不变数据变化前后需使用同一个引用。

具体解释

1> 使用PureComponent不要在props和state中改变对象和数组这种引用类型。即可变数据不能使用同一个引用。如果你在你的父组件中改变对象,你的“pure”子组件不将更新。虽然值已经被改变,但是子组件比较的是之前props的引用是否相同,所以不会检测到不同。因此,你可以通过使用es6的assign方法或者数组的扩展运算符或者使用第三方库,强制返回一个新的对象。

2> 不要在render的函数中绑定值。即不变数据变化前后需使用同一个引用。

假设你有一个项目列表,每个项目都传递一个唯一的参数到父方法。为了绑定参数,你可能会这么做:

<CommentItem likeComment={() => this.likeComment(user.id)} />

这个问题会导致每次父组件render方法被调用时,一个新的函数被创建,已将其传入likeComment。这会有一个改变每个子组件props的副作用,它将会造成他们全部重新渲染,即使数据本身没有发生变化。

为了解决这个问题,只需要将父组件的原型方法的引用传递给子组件。子组件的likeComment属性将总是有相同的引用,这样就不会造成不必要的重新渲染。

<CommentItem likeComment={this.likeComment} userID={user.id} />

然后再子组件中创建一个引用了传入属性的类方法:

class CommentItem extends PureComponent { ... handleLike() { this.props.likeComment(this.props.userID) } ... }

3> 不要在render方法里派生数据。即不变数据变化前后使用需同一个引用。

考虑一下你的配置组件将从一系列文章中展示用户最喜欢的十篇文章。

render() { const { posts } = this.props const topTen = posts.sort((a, b) => b.likes - a.likes).slice(0, 9) return //... }

每次组件重新渲染时topTen都将有一个新的引用,即使posts没有改变并且派生数据也是相同的。这将造成列表不必要的重新渲染。

你可以通过缓存你的派生数据来解决这个问题。例如,设置派生数据在你的组件state中,仅当posts更新时它才更新。

componentWillMount() { this.setTopTenPosts(this.props.posts) } componentWillReceiveProps(nextProps) { if (this.props.posts !== nextProps.posts) { this.setTopTenPosts(nextProps) } } setTopTenPosts(posts) { this.setState({ topTen: posts.sort((a, b) => b.likes - a.likes).slice(0, 9) }) }

如果你正在使用Redux,可以考虑使用reselect来创建"selectors"来组合和缓存派生数据。

4> 子组件数据更新,使用Immutable.js库解决数据不变问题。

有时候数组或对象内部依旧持有的是数组或对象,数据引用变化,虽然指针变了,但是内层数据实际上没变化,此时也会触发render。这个时候可以使用immutable-js函数库。

5> 复杂状态与简单状态不要共用一个组件

这点可能和 PureComponent 没多少关系,但做的不好可能会浪费很多性能,比如一个页面上面一部分是一个复杂的列表,下面是一个输入框,抽象代码:

代码语言:javascript
复制
change = (e) => {
  this.setState({ value: e.target.value });
}
render() {
  return (<div>
    <ul>
      {this.state.items.map((i, k) => <li key={k}> {...}</li>)}
    </ul>
    <input value={this.state.value} onChange={this.change} />
  </div>)
}

表单和列表其实是没有什么关联的,表单的值也可能经常变动,但它的会给列表也带来必然的 diff操作,这是没必要的,最好是给列表抽出成一个单独的 PureComponent 组件,这样 state.items不变的话,列表就不会重新 render 了。

PureComponent与shouldComponentUpdate共存

如果 PureComponent 里有 shouldComponentUpdate 函数的话,直接使用 shouldComponentUpdate 的结果作为是否更新的依据,没有 shouldComponentUpdate 函数的话,才会去判断是不是 PureComponent ,是的话再去做 shallowEqual 浅比较。

原则

  1. 虽然通常情况下易变性就是不好的,但是当使用PureComponent时问题会变得复杂。尽量让数据不可变,可以使用Immutable.js。
  2. 如果你在render方法中创建一个新的函数、对象或者是数组,那么你的做法(可能)是错误的。
  3. PureComponent不仅会影响本身,而且会影响子组件,所以PureComponent最佳情况是展示组件。(关于展示组件可以参考https://juejin.im/post/5a52fe32f265da3e317e008bhttps://juejin.im/post/5af3d66ef265da0b7b35ec1d

浅比较

shallowEqual会比较 Object.keys(state | props) 的长度是否一致,每一个 key 是否两者都有,并且是否是一个引用,也就是只比较了第一层的值,确实很浅,所以深层的嵌套数据是对比不出来的。

shallowEqual源码

代码语言:javascript
复制
const hasOwn = Object.prototype.hasOwnProperty

function is(x, y) {
  if (x === y) {
    return x !== 0 || y !== 0 || 1 / x === 1 / y
  } else {
    return x !== x && y !== y
  }
}

export default function shallowEqual(objA, objB) {
  if (is(objA, objB)) return true

  if (typeof objA !== 'object' || objA === null ||
      typeof objB !== 'object' || objB === null) {
    return false
  }

  const keysA = Object.keys(objA)
  const keysB = Object.keys(objB)

  if (keysA.length !== keysB.length) return false

  for (let i = 0; i < keysA.length; i++) {
    if (!hasOwn.call(objB, keysA[i]) ||
        !is(objA[keysA[i]], objB[keysA[i]])) {
      return false
    }
  }

  return true
}

参考

PureComponent

https://segmentfault.com/a/1190000014979065 https://wulv.site/2017-05-31/react-purecomponent.html https://juejin.im/post/5b614d9bf265da0fa759e84b

浅比较 https://www.imweb.io/topic/598973c2c72aa8db35d2e291

展示组件和容器组件 https://juejin.im/post/5a52fe32f265da3e317e008b https://juejin.im/post/5af3d66ef265da0b7b35ec1d

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • PureComponent原理
  • PureComponent注意事项
    • 具体解释
      • PureComponent与shouldComponentUpdate共存
        • 原则
        • 浅比较
          • shallowEqual源码
          • 参考
          相关产品与服务
          容器服务
          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档