React Native 函数式组件优化方式

React 性能优化理念的主要方向简单概括为两个:

  1. 减少重新 render 的次数。因为在 React 里最重(花时间最长)的一块就是 reconciliation(简单可以理解为 diff),如果不 render,就不会 reconciliation。
  2. 减少计算的量。主要是减少重复计算,对于函数式组件来说,每次 render 都会重新从头开始执行函数调用。

关于类组件的渲染优化,普遍的方式主要是使用:shouldComponentUpdate 和PureComponent,解决思路都是减少重新 render 的次数(减少父组件更新而导致子组件更新)虽然也可以在 state 更新的时候阻止当前组件渲染,如果这么做,证明该属性不适合作为 state,而应作为静态属性或者全局变量。

减少重复 render

在函数式组件里面没有生命周期也没有类, 类似的优化可以使用 React.memo 来实现

React.memo

 简单理解

      对标类组件里面的 PureComponent,可以减少重新 render 的次数。

 解决的问题

      子组件在 props 没有变化的情况下,父组件重新渲染,子组件也不应该渲染。

基本定义

      React.memo 在相同 props 的情况下渲染相同的结果,通过记忆组件渲染结果的方式提高组件性能。

将组件通过React.memo包装即可,React.memo 是一个高阶函数,传递一个组件,返回一个可记忆组件

const GeneboxComponent = (props) => {
   /* 使用 props 渲染 */
   return (
     <Text>{props.txt}</Text>
   )
}
export default React.memo(GeneboxComponent);

默认情况下 React.memo 只会对 props 的复杂对象做浅比较(浅比较就是只会对比前后两次 props 对象引用是否相同,不会对比对象里面的内容是否相同),如果你想要控制对比过程,需要自定义的比较函数通过第二个参数传入来实现。

注意: 如果 props 相等,areEqual 返回 true;如果 props 不相等,返回 false。与 shouldComponentUpdate 返回值相反。

useCallback

在函数式组件里每次重新渲染,函数组件都会重新开始执行,如果通过 props 传递了callback 函数, 每次创建callback都是不同的引用,会导致子组件重新渲染。

看个例子:

// 父组件 GeneboxApp.js
const GeneboxApp = () => {

  // callback
  const callback = () => {
    setState({});
  };

  return (
     <GeneboxComponent onClick={callback} />
  )
}

// 子组件 GeneboxComponent.js
const GeneboxComponent = (props) => {
   /* 使用 props 渲染 */
   return (
     <Text onPress={props.onClick}>
       {props.txt}
     </Text>
   )
}


export default React.memo(GeneboxComponent)

解决办法:

使用 useCallback 保持两个函数的引用一致。把函数以及依赖项作为参数传入 useCallback,返回该回调函数的 memoized 版本 => memoizedCallback, memoizedCallback 只有在依赖项有变化的时候才会更新。

还拿上面的代码为例:

// 父组件 GeneboxApp.js
const GeneboxApp = () => {

  // callback
  const callback = () => {
    setState({});
  };
  
  // 第一个参数为处理回调, 第二个参数为回调的参数, 如果没有参数可以传[], 参数会进行缓存处理
  const memoizedCallback = useCallback(callback, [])


  return (
     <GeneboxComponent onClick={memoizedCallback} />
  )
}

// 子组件 GeneboxComponent.js
const GeneboxComponent = (props) => {
   /* 使用 props 渲染 */
   return (
     <Text onPress={props.onClick}>
       {props.txt}
     </Text>
   )
}

export default React.memo(GeneboxComponent)

StyleSheet.compose

static compose(style1: object, style2: object): object | array<object>

在向子组件传递多个Style时, 我们一般会写出如下的代码:

<Widget style={[ ...aStyle, ...bStyle ]} />

上述代码在父组件的多次render中, style都会被生成为一个新的数组, 所以就算在子组件中做了类似pure的比较, 也无法避开重复的render刷新。同样, 不使用StyleSheet.create创建的Style, 也会存在此类问题, 通常,通过StyleSheet创建的Style, RN可以通过ID进行内存优化。 直接引用样式对象将失去这些优化。

StyleSheet的compose函数可以组合两种样式,style2将覆盖style1中的任何样式。 并保持PureComponent检查的引用相等。 

<Widget style={StyleSheet.compose(aStyle, bStyle);} />

StyleSheet.flatten

compose函数只能处理两个Style, 如果涉及多个style, 则需要使用 flatten 函数解决。flatten 函数会将一组样式对象展平为一个聚合的样式对象。同时, 也可以使用此方法查找由StyleSheet.register返回的ID。

<Widget style={StyleSheet.flatten([aStyle, bStyle, cStyle]);} />

减少重复计算逻辑

直接看个例子

// 子组件 GeneboxComponent.js
GeneboxComponent = (props) => {


   // 存在大量计算, 且重复
   cal = () => { 
      for (let i = 0; i < 1000000; i++) { 
          result += i; 
      } 
   }


   const result = cal();


   return (
     <Text onPress={props.onClick}>
       {result}
     </Text>
   )
}


export default React.memo(GeneboxComponent)
 

cal中存在大量计算, 切每次render都会重复执行, 此时需要将其计算进行缓存, 避免多次重复计算

useMemo

// 子组件 GeneboxComponent.js
GeneboxComponent = (props) => {
   // 存在大量计算, 且重复
   cal = () => {
     ...
   }
   // 第二个参数为第一个函数的参数
   const result = useMemo(cal, [])
   return (
     <Text onPress={props.onClick}>
       {result}
     </Text>
   )
}
export default React.memo(GeneboxComponent)
 

使用 useMemo 进行计算结果缓存, 如果数组里面的值(即函数参数)发生变化,会重新执行函数,并将函数返回的值缓存起来并作为 useMemo 的返回值 。

发布了214 篇原创文章 · 获赞 371 · 访问量 92万+

猜你喜欢

转载自blog.csdn.net/u013718120/article/details/105511305