前言
最近在筹备面试。温习了一些 react 的知识点,特此总结。
开始
React 生命周期
react 16 以前的生命周期是这样的
组件在首次渲染时会被实例化,而后调用实例下面的 componentWillMount,render 和 componentDidMount 函数。组件在更新渲染时能够调用 componentWillReceiveProps,shouldComponentUpdate,componentWillUpdate,render 和 componentDidUpdate 函数。组件在卸载时能够调用 componentWillUnmount 函数。
借图:
从 React v16.3 开始,React 倡议应用 getDerivedStateFromProps
和getSnapshotBeforeUpdate
两个生命周期函数代替 componentWillMount
,componentWillReceiveProps
和 componentWillUpdate
三个生命周期函数。这里须要留神的是 新增的两个生命周期 函数和原有的三个生命周期函数必须离开应用,不能混合应用
目前的生命周期(借图):
componentWillMount 存在的问题
有人认为在 componentWillMount 中能够提前进行异步申请, 防止白屏。然而 react 在调用 render 渲染页面的时候,render 并不会期待异步申请完结,再获取数据渲染
。这么写是有潜在隐患的。
而在 react fiber 之后 可能在一次渲染中屡次调用。起因是:react fiber 技术应用增量渲染来解决掉帧的问题,通过 requestIdleCallback 调度执行每个工作单元,能够中断和复原,生命周期一旦中断,复原之后会从新跑一次之前的生命周期
新的生命周期
static getDerivedStateFromProps
- 触发工夫(v16.4 修改):组件每次被 rerender 的时候,包含在组件构建之后(render 之前最初执行),每次获取新的 props 或 state 之后。在 v16.3 版本时,组件 state 的更新不会触发该生命周期
- 每次接管新的 props 之后都会返回一个对象作为新的 state,返回 null 则阐明不须要更新 state
- 配合 componentDidUpdate,能够笼罩 componentWillReceiveProps 的所有用法
getSnapshotBeforeUpdate
- 触发工夫: update 产生的时候,在 render 之后,在组件 dom 渲染之前。
- 返回一个值,作为 componentDidUpdate 的第三个参数。
- 配合 componentDidUpdate, 能够笼罩 componentWillUpdate 的所有用法。
React Fiber
因为 React 渲染 / 更新过程一旦开始无奈中断,继续占用主线程,主线程忙于执行 JS,得空他顾(布局、动画),造成掉帧、提早响应(甚至无响应)等不佳体验。fiber 应运而生。
Fiber 是对 react reconciler(和谐)外围算法的重构。要害个性如下:
- 增量渲染(把渲染工作拆分成块,匀到多帧)
- 更新时可能暂停,终止,复用渲染工作
- 给不同类型的更新赋予优先级
- 并发方面新的根底能力
增量渲染用来解决掉帧的问题,渲染工作拆分之后,每次只做一小段,做完一段就把工夫控制权交还给主线程,而不像之前长时间占用。
Fiber tree
- Fiber 之前的 reconciler(被称为 Stack reconciler)自顶向下的递归 mount/update,无奈中断(继续占用主线程),这样主线程上的布局、动画等周期性工作以及交互响应就无奈立刻失去解决,影响体验。
- Fiber 解决这个问题的思路是把渲染 / 更新过程(递归 diff)拆分成一系列小工作,每次查看树上的一小部分,做完看是否还有工夫持续下一个工作,有的话持续,没有的话把本人挂起,主线程不忙的时候再持续。
fiber 树其实是一个单链表构造
,child 指向第一个子节点,return 指向父节点,sibling 指向下个兄弟节点。构造如下:
// fiber tree 节点构造
{
stateNode,
child,
return,
sibling,
...
}
Fiber reconciler
reconcile 过程分为 2 个阶段:
1.(可中断)render/reconciliation 通过结构 workInProgress tree 得出 change
2.(不可中断)commit 利用这些 DOM change(更新 DOM 树、调用组件生命周期函数以及更新 ref 等外部状态)
构建 workInProgress tree 的过程就是 diff 的过程,通过 requestIdleCallback 来调度执行一组工作,每实现一个工作后回来看看有没有插队的(更紧急的),每实现一组工作,把工夫控制权交还给主线程,直到下一次 requestIdleCallback 回调再持续构建 workInProgress tree
生命周期也被分成了两个阶段:
// 第 1 阶段 render/reconciliation
componentWillMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
// 第 2 阶段 commit
componentDidMount
componentDidUpdate
componentWillUnmount
第 1 阶段的生命周期函数可能会被屡次调用,默认以 low 优先级执行,被高优先级工作打断的话,稍后从新执行。
fiber tree 与 workInProgress tree
双缓冲技术:指的是 workInProgress tree 结构结束,失去的就是新的 fiber tree,而后把 current 指针指向 workInProgress tree,因为 fiber 与 workInProgress 相互持有援用,旧 fiber 就作为新 fiber 更新的预留空间,达到复用 fiber 实例的目标。
每个 fiber 上都有个 alternate 属性,也指向一个 fiber,创立 workInProgress 节点时优先取 alternate,没有的话就创立一个
let workInProgress = current.alternate;
if (workInProgress === null) {
//...
workInProgress.alternate = current;
current.alternate = workInProgress;
} else {
// We already have an alternate.
// Reset the effect tag.
workInProgress.effectTag = NoEffect;
// The effect list is no longer valid.
workInProgress.nextEffect = null;
workInProgress.firstEffect = null;
workInProgress.lastEffect = null;
}
这么做的益处:
- 可能复用外部对象(fiber)
- 节俭内存调配、GC 的工夫开销
fiber 中断 复原
中断:查看以后正在解决的工作单元,保留以后成绩(firstEffect, lastEffect),批改 tag 标记一下,迅速收尾并再开一个 requestIdleCallback,下次有机会再做
断点复原:下次再解决到该工作单元时,看 tag 是被打断的工作,接着做未实现的局部或者重做
P.S. 无论是工夫用尽“天然”中断,还是被高优工作粗犷打断,对中断机制来说都一样。
React setState
在代码中调用 setState 函数之后,React 会将传入的参数对象与组件以后的状态合并,而后触发所谓的和谐过程(Reconciliation)。通过和谐过程,React 会以绝对高效的形式依据新的状态构建 React 元素树并且着手从新渲染整个 UI 界面。在 React 失去元素树之后,React 会主动计算出新的树与老树的节点差别,而后依据差别对界面进行最小化重渲染。在差别计算算法中,React 可能绝对准确地晓得哪些地位产生了扭转以及应该如何扭转,这就保障了按需更新,而不是全副从新渲染。
setState 调用时有时是同步的(settimeout, 自定义 dom 事件),有时是异步的(一般调用)
React 事件机制
React 事件是通过事件代理,在最外层的 document 上对事件进行对立散发,并没有绑定在实在的 Dom 节点上。
而且 react 外部对原生的 Event 对象进行了包裹解决。具备与浏览器原生事件雷同的接口,包含 stopPropagation()
和 preventDefault()
。