阅读 350

react性能优化之异常render次数

react性能优化之异常render次数

react工程的性能优化部分:

  • 除了前端通用的,比如减少dom节点,懒加载,按需加载之外。

  • react有特别之处就是:可能会有莫名的页面假死(不能操作)。大多情况都是过多的意外render,导致过多的js执行,阻塞了ui的渲染

以下4种常见的情况,容易触发过多异常render,损害性能

使用pure组件(React.PureComponent / React.Memo)

不使用pure组件(React.PureComponent / React.Memo),容易造成过多异常的render

举例

class App extends React.Component {   constructor() {     super()     this.state = {       aa: 123,       bb: 222,       obj: {a: 1}     }   }   render () {     console.log('render App')     return (       <Provider store={store}>         <div className="App" onClick={() => { // 此处点击触发,A B 子组件都会触发render           this.setState({             aa: 123           })         }}>           <A aa={this.state.aa} bb={this.state.obj}/>           <B bb={this.state.bb}/>         </div>       </Provider>     );   } } // 子组件A  class A extends React.Component {   render () {     console.log('render A')     console.log(this.props)     return (       <div>         {this.props.aa}       </div>     );   } } // 子组件B const UserContainer = (props) => {   console.log('render 222')   console.log(props)   return (     <div>       <h2>{props.bb}</h2>     </div>   ) } 复制代码

问题点:

  • 当我们点击 className="App" 内容区,触发了 this.setState 后,A 和 B 组件都异常重新render了!!

我们理想的目标是:

  • A和B都不触发渲染因为 this.state.bb 根本没动, this.state.aa的值也没变

    • 此处如果不是onClick事件,是onScoll 或 onResize 这种不做防抖的话,很容易触发N次,然后会触发N次render,页面容易直接卡死

解决办法:用 React.PureComponent 或 React.Memo

  • 会自动对props和state的值做一个浅比较如果值的前后没改变,则不会触发render(另外插一嘴vue没有这个问题,可以理解为,vue自动已经处理了)

    • 另外使用了context的话,还是会穿透 并 触发render。是react的正常render。所以context不能滥用(官方推荐实用场景:当前认证的用户、主题或首选语言)

    • 如果想要控制某些props,即使被改变,也不触发render。做法是:类组件用shouldComponentUpdate。函数组件用React.Memo的第二个参数控制。可以自行了解

避免使用匿名函数

function App() {   const [aa, setAa] = useState(123)   return (       <div className="App">         // 子组件A         <A onClick={() => {           console.log('我可能会发请求调接口, 然后处理很多逻辑')         }} />         <div onClick={() => setAa(aa+1)}>点击触发render</div>       </div>   ); } export default React.memo(App); 复制代码

问题点:

  • 当前组件每次render的时候,匿名函数都会被重新创建,会导致A组件被触发render(特别是当A组件放在循环内,更是会触发多次)

我们想要的应该是:

  • A组件不会被触发render

解决办法

  • 不用匿名函数,用 函数的引用 + useCallback 即可 (把函数引用缓存起来,否则每次render都会重新声明 函数引用)

function App() {   const [aa, setAa] = useState(123)   const fn = useCallback(() => {       console.log('发请求调接口, 然后处理很多逻辑')   }, [])   return (       <div className="App">         // 子组件A         <A onClick={fn} />         <div onClick={() => setAa(aa+1)}>点击触发render</div>       </div>   ); } export default React.memo(App); 复制代码

避免使用内联对象

原因是:每次render时,对象的引用都会改变。一但引用发生改变,会导致以下A、B、C 3个子组件都触发不必要的render

function App() {   const [aa, setAa] = useState(123)   console.log('render App')      const style = { margin: 0 }   const propsObj = { someProp: 'someValue', a: {} }   return (       <div>         <A style={{ margin: 0 }} />         <B style={style} />         <C {...propsObj} />         <div onClick={() => setAa(aa+1)}>点击我,触发App组件的render</div>       </div>   ); } export default React.memo(App); 复制代码

改进写法

  1. 静态的对象,可以放组件外面

  2. 或者对象统一由useState声明(class组件是放state内),useState内部会保存当前对象状态,并做浅比较

const style = { margin: 0 } // 静态的对象,可以放App组件外面 function App() {   const [aa, setAa] = useState(123)   console.log('render App')      const [bb, setBb] = useState({     someProp: 'someValue', a: {}   })   return (       <div>         <A style={style} {...bb} />         <div onClick={() => setAa(aa+1)}>点击我,触发App组件的render</div>       </div>   ); } export default React.memo(App); 复制代码

不要再react控制范围之外,多次setState

比如在setTimeout内,执行N次setState,就会触发N次render。理想的情况是,只异步render 一次

详细可以看我另一篇:juejin.cn/post/706215…


作者:bigtree
链接:https://juejin.cn/post/7068877716224901128


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐