共计 2579 个字符,预计需要花费 7 分钟才能阅读完成。
1、这两个 hook 是为了解决什么问题?
function 组件和 class 组件性能优化其实要优化的点是相似的。
class 组件性能优化的点:
- 调用 this.setState,就会触发组件的从新渲染,无论前后 state 是否雷同
- 父组件更新,子组件也会自动更新
在 class 组件中,咱们能够应用 immutable 去创立数据,用继承 pureComponent 的形式对 props 浅比拟和用 shouldComponentUpdate 判断前后的 props 和 state, 如果没有发生变化, 就返回 false 来阻止更新。
然而在 function 组件中,就没有 shouldComponentUpdate 这个生命周期了,就无奈判断前后的 props 是否雷同,从而是不是要从新渲染。useEffect 函数也没有辨别是以后是 mount 还是 update, 这样一来,就意味着,每次组件的更新都会执行其简单的逻辑,性能的耗费将是十分大的。
所以为了解决这个问题,useCallback 和 useMemo 就呈现了。
useCallback、useMemo、useEffect 这三者的参数是统一的。useEffect 的第一个参数 effect,是能够解决副作用的。而 useCallback 和 useMemo 是不能够解决副作用的。
useCallback 和 useMemo 都会在组件第一次渲染的时候执行,之后会在其依赖的变量产生扭转时再次执行;并且这两个 hooks 都返回缓存的值,useMemo 返回缓存的变量,useCallback 返回缓存的函数。
2、React.memo() 和这两个 hook 的区别
在 class 组件的时代,咱们通过上面两个办法去做性能优化。
- pureComponent,对 props 进行浅比对
- shouldComponentUpdate,对 props 和 state 进行比拟,依据返回值来解决是否要更新
对于还没有 hook 的 function 组件,咱们也有办法对其进行性能优化。react 提供了 React.memo 这样的高阶组件。它与 pureComponent 和类似,然而,这个高阶组件并不适用于 class 组件,而只为 function 组件服务。相比于 PureComponent,React.memo() 能够反对指定一个参数,能够相当于 shouldComponentUpdate 的作用,因而 React.memo() 绝对于 PureComponent 来说,用法更加不便。
function MyComponent () {}
function isEqual(prevProps, nextProps) {
/*
如果 nextProps 和 prevProps 的后果雷同,那么就会返回 true
不相等,就返回 false
*/
}
export default React.memo(MyComponent, isEqual);
看下面的代码,其实,用法很简略。在 Function Component 之外,在申明一个 isEqual 办法来判断两次 props 有什么不同,如果第二个参数不传递,则默认只会进行 props 的浅比拟。最终 export 的组件,就是 React.memo() 包装之后的组件。在某些状况下,React.memo 的第二个参数是必须的。
总的来说,React.memo() 办法,是让整个组件要不要 re-render,记住这一点很重要,然而,有的时候,咱们想要组件的某一部分要不要 re-render,而不是整个组件要不要 re-render,怎么办?那就用就是 useMemo()。
3、useMemo() 细粒度性能优化
函数签名:
function useMemo<T>(factory: () => T, deps: DependencyList | undefined): T;
useMemo 的第一个参数是一个工厂函数,如果传递了依赖值列表,那么在在依赖值列表发生变化时候,这个工厂函数就会执行。
用法:
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
useMemo() 返回的是一个 memoized 值,只有当依赖项(比方下面的 a,b 发生变化的时候,才会从新计算这个 memoized 值)
memoized 值不变的状况下,不会从新触发渲染逻辑。说到渲染逻辑,须要记住的是 useMemo() 是在 render 期间执行的 ,所以不能进行一些额定的副操作,比方网络申请等。如果没有提供依赖数组(下面的 [a,b])则每次都会从新计算 memoized 值,也就会 re-redner。
4、useCallback 函数
函数签名:
function useCallback<T extends (...args: any[]) => any>(callback: T, deps: DependencyList): T;
useCallback 的第一个参数是一个函数,这个函数也是 useCallback 去缓存的对象。每次依赖值列表发生变化的时候,useCallback 会去更新这个函数,然而并不会去执行。
用法:
const fnA = useCallback(fnB, [a])
useCallback 的用法其实和 useMemo 是一样的,然而他们惟一的区别是,useCallback 是缓存了函数。
场景:在开发中,你会遇到这样的场景,须要从父组件中传递一个函数到子组件,如果咱们不必 useCallback 包裹,那么也就意味着,只有父组件有更新,都会向子组件去传递一个新函数,尽管说每次传递的函数都一样,然而仍旧是俩个不同的对象。然而如果应用了 useCallback, useCallback 就会依据依赖项是否发生变化,从而决定是否返回一个新的函数,函数外部作用域也随之更新。
5、总结
- 在子组件不须要父组件的值和函数的状况下,只须要应用 React.memo 函数包裹子组件即可。
- 如果有函数传递给子组件,应用 useCallback
- 如果有值传递给子组件,应用 useMemo
- useEffect、useMemo、useCallback 都是自带闭包的。也就是说,每一次组件的渲染,其都会捕捉以后组件函数上下文中的状态 (state, props),所以每一次这三种 hooks 的执行,反映的也都是以后的状态,你无奈应用它们来捕捉上一次的状态。对于须要捕捉上一次状态值的状况,咱们应该应用 ref 来拜访。