React组件的渲染优化问题

102 阅读1分钟

渲染优化

useContext

我们先看一个传统的Context的渲染例子。

import React,{createContext,useContext,useState} from "react"
import { ColorBlock,Button,Box } from "x-react-components"

const ctx = createContext({
  firstName:"Zhang",
  lastName:"Fisher",
  age:18
})

const Child = (props:any)=>{
    const context=useContext(ctx)
    return <ColorBlock name={`子组件:${props.name}`}>
      <span>Hello :{context.firstName}{' '}{context.lastName}</span> 
    </ColorBlock>
}


let count:number = 0
export default ()=>{
  const [user,setUser] = useState({
    firstName:"Zhang",
    lastName:"Fisher",
    age:18
  })
  return <ctx.Provider value={user}>
      <Box title="根组件">
        <ColorBlock name={'FullName'}>{user.firstName}{' '}{user.lastName}</ColorBlock>
        <ColorBlock name={'Age'}>Age :{user.age}</ColorBlock>
      </Box>
      <Box title="子组件">
        <Child name="A"/>
        <Child name="B"/>
      </Box>
      <Button onClick={()=>{
        setUser({firstName:"Zhang",lastName:"Fisher",age:++count})
      }}>+Age</Button>
    </ctx.Provider>
}

从上面的例子可看到,当更新Context.age时,所有的子组件不管是否有使用Age均会重新渲染,而这是不必要的,因为子组件并没有使用到Context的数据,为此我们一般需要使用React.memo或一些第三方库来进行优化渲染。 此方案最大的问题在于,当更新根Context时,所有的子组件都会重新渲染,这是不必要的,因为子组件并没有使用到根Context的数据。我们希望能实现更细粒度的渲染,只有当子组件使用到的数据发生变化时,才会重新渲染

React.Memo

为了优化渲染逻辑,一般我们会使用`React.memo来进行优化渲染。

const Child = React.memo((props:any)=>{     
    const context=useContext(ctx)     
        return <ColorBlock name={`子组件:${props.name}`}>       
        <span>Hello :{context.firstName}{' '}{context.lastName}</span>      
        </ColorBlock> 
    },(prop,oldProp)=>{
        return .....  
    }
  • 经过优化后,当更新Age时,仅根组件会重新渲染,而FirstNameLastName不会重新渲染,因为它们并没有使用到Age
  • 当在根组件中更新FirstName时,仅FirstName会重新渲染。而LastName组件中没有FirstName,所以不会重新渲染。 在大型React应用,面对复杂的状态变化,如何决定何时使用React.memo是一个很大的心智问题,也是最容易搞坑里的,这也是为什么React官方要推Compiler的原因**

信号组件

而更好的办法就是最近比较流行的signal机制,signal机制可以将渲染颗粒度限定在组件范围,只有使用到数据的组件才会重新渲染。 基于Signal,渲染颗粒度可以是组件中的一个片段或ReactNode,更加精细,更加高效。 了解更多关于Signal的内容,可以阅读深入解析:React中的信号组件与细粒度更新