一、React Hook

1.要解决什么问题?

1.可以在函数组件中使用状态

2.可以复用组件状态及相关的变更逻辑。因为class组件的状态变更是分散在各个生命周期中的,又或者是通过高阶组件以props传进来的,当被多个高阶组件包装时,难以区分props的来源。

应hooks的典型场景:

很多页面都会用到表格,这些表格的数据都来自于服务端,加载数据时界面会出菊花,数据加载完了,隐藏菊花。这种公共逻辑就特别适合进行提取。

 

2. 使用注意事项

不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确。

这样做的原因是因为: React 靠的是 Hook 调用的顺序来确定state应该和哪个useState对应,因此Hook 的调用顺序在多次渲染之间应保持一致。

3.底层原理

文章

 

二、React的state问题

文章

当使用setTimeout、setInterval、或者直接在原生事件内部直接setState时,此时react会同步更新state

 

三、React性能优化技术

核型思想就是让组件避免不必要的重复渲染,举例来说,当父组件render时,即使子组件没有发生任何变化,还是会重渲染。具体有以下优化措施:

1.子组件上绑定的属性值是对象、函数时,不要使用内联模式,不然父组件每次render,即使属性值没发生变化,都会导致子组件渲染。

2. 函数组件的优化:

  • 使用React.memo(fnComponent)这个高阶组件来做记忆函数,当props不变时,直接复用上一次的渲染结果。对props的比较是浅比较,可以自己实现比较逻辑,文档说明
  • 组件内部如果涉及到复杂耗时的数据计算,可以使用useMemo来缓存计算结果
  • 若内部有子组件,并且子组件需要接收函数做为属性值的话,此时父组件中的回调函数需要使用useCallback函数,不然会导致子组件的渲染优化判断机制失败。

3. class组件非必要情况下,不要使用state;可以在shouldComponentUpdate里判断props或state是否发生了改变,或者继承自PureComponent

4. 使用React.lazy来延迟加载非必需的组件,典型场景是路由系统中

const Foo = React.lazy(() => import('../componets/Foo));

React.lazy不能单独使用,需要配合React.suspense,suspence是用来包裹异步组件,添加loading效果等。

<React.Suspense fallback={<div>loading...</div>}>
    <Foo/>
</React.Suspense>

5. 使用循环来生成element元素时,应使用key,以便react diff算法能识别出同级元素是否只是位置发生了变化。

6. 减少不必要的dom嵌套,在react中多个同级元素需要一个父元素,为了满足这个规则,会构造无用的dom。应该使用<React.Fragment></React.Fragment>

7. 使用css来隐藏节点,而不是真的移除或添加DOM节点, 组件重新初始化一次的代价很高

8  如果有多个子或孙组件需要使用同一个state,此时应使用redux,而不是将公共的state移到祖先组件中,否则组件层次太深的话,在祖先组件setState会导致无数个子孙组件的render方法再次被调用

 react性能优化实战

 

四、ReactDom Diff算法

算法实现

另一篇

若采用常规的树比较方法,时间复杂度是O(n^3),React基于一些假设,将时间复杂度变为O(n),相应的策略有:

1. 对两颗vdom树,采用逐层比较策略

react项目怎么使用rem react 技巧_react项目怎么使用rem

不存在跨层级的节点移动,因此节点间只存在添加、删除操作。如下图所示,当想把A移动到D下面时,先删除A,然后再到D节点中创建A

react项目怎么使用rem react 技巧_性能优化_02

 

2. 两个不同类型的元素,生成的树肯定是不同的,当某一层级两个元素的类型不同,就直接生成该节点及子树,没有必要再递归比较了。如下图:当component D换成了component G 后,即使两者的结构非常类似,也会将D删除再重新创建G

react项目怎么使用rem react 技巧_react项目怎么使用rem_03

 

3.对于同一层级的一组子节点,通过指定唯一的key进行区分。通过key可以准确地发现新旧集合中的节点是否只存在位置不同的情况,若是,只需要将旧集合中节点的位置进行移动,更新为新集合中节点的位置即可,无需进行节点删除和插入。

 

基于key的移动算法策略: 如果当前节点在新集合中的位置比老集合中的位置靠前的话,是不会影响后续节点操作的,因此不进行位置移动(例如下图中的B和d):

 

 

react项目怎么使用rem react 技巧_性能优化_04

 

diff过程如下:

    使用一个变量(下面假设为lastIndex)来记录在新集合访问过的节点中,其在老集合的最大下标,那么需要移动的节点,就应该移动到该位置,初始值为0。

    循环新集合

  • 节点B:  lastIndex=0,   oldIndex=1; lastIndex < oldindex,因此B节点不动,lastIndex= Math.max(oldIndex, lastIndex) = 1
  • 节点A:lastIndex=1, oldIndex=0;lastIndex > oldIndex,因此A节点进行移动操作,lastIndex= Math.max(oldIndex, lastIndex) = 1
  • 节点D:lastIndex=1,   oldIndex=3;lastIndex < oldindex,因此D节点不动,lastIndex= Math.max(oldIndex, lastIndex) = 3
  • 节点C:lastIndex=3,   oldIndex=2;lastIndex > oldindex ,因此C节点进行移动操作。循环结束

五、渲染原理

react项目怎么使用rem react 技巧_性能优化_05

 

渲染原理

六、fiber架构

原理