2024年前端最全深入 React Fiber 架构和调和算法(1),高级前端口试答案 ...

打印 上一主题 下一主题

主题 828|帖子 828|积分 2484

css

1,盒模型
2,如何实现一个最大的正方形
3,一行水平居中,多行居左
4,水平垂直居中
5,两栏布局,左边固定,右边自顺应,左右不重叠
6,如何实现左右等高布局
7,画三角形
8,link @import导入css
9,BFC理解

js

1,判断 js 范例的方式
2,ES5 和 ES6 分别几种方式声明变量
3,闭包的概念?优缺点?
4,浅拷贝和深拷贝
5,数组去重的方法
6,DOM 变乱有哪些阶段?谈谈对变乱代理的理解
7,js 执行机制、变乱循环
8,介绍下 promise.all
9,async 和 await,
10,ES6 的 class 和构造函数的区别
11,transform、translate、transition 分别是什么属性?CSS 中常用的实现动画方式,
12,介绍一下rAF(requestAnimationFrame)
13,javascript 的垃圾接纳机制讲一下,
14,对前端性能优化有什么了解?一样平常都通过那几个方面去优化的?

开源分享:【大厂前端口试题分析+焦点总结学习笔记+真实项目实战+最新解说视频】
}
可以在这里[8]把玩这段代码,如你所见,这是一个简朴组件,从 render() 方法中返回两个子元素 button 和 span 。
当你单击按钮时,组件的状态将被内部的 handler 更新,顺带的,这会导致 span 元素的文本更新。
React 在 调和(reconciliation) 期间执行各种活动。
比方,下面是 React 在简朴组件的初次渲染中,以及 state 更新之后,执行的高级操作:
•更新 ClickCounter 组件中 state 的 count 属性。•检索并比力 ClickCounter 的子组件和 props 。•更新 span 的 props 。
调和(reconciliation) 期间也举行了其他活动,包罗调用生命周期方法[9]或更新 refs[10]。这些活动在 Fiber 架构中被统称为 work。 work 的 type 通常取决于 React 元素的范例。
比方,对一个类组件而言,React 必要创建一个实例,而函数组件则无需执行此操作。
React 的有许多范例的 elements,比方类组件和函数组件, host 组件(DOM节点)等。
React 元素的范例由传入到 createElement 的第一个参数决定,通常在 render 方法中使用此函数来创建元素。
在我们开始探索活动细节和重要的 fiber 算法之前,让我们先熟悉 React 内部使用的数据布局。
从 React 元素到 Fiber 节点

React 中的每个组件都有一个 UI 表示,我们可以称之为从 render 方法返回的一个视图或模板。
这是 ClickCounter 组件的模板:
Update counter
{this.state.count}
React 元素

模板通过JSX编译器后,将得到一堆React元素。下面是真正从 React 组件的 render 方法返回的结果(并不是 HTML)。
由于我们不必要使用JSX,因此可以将 ClickCounter 组件的 render 方法重写:
class ClickCounter {

render() {
return [
React.createElement(
‘button’, {
key: ‘1’,
onClick: this.onClick
},
‘Update counter’
),
React.createElement(
‘span’, {
key: ‘2’
},
this.state.count
)
]
}
}
render 方法调用的 React.createElement 会产生两个数据布局。
像这样:
[{
$$typeof: Symbol(react.element),
type: ‘button’,
key: “1”,
props: {
children: ‘Update counter’,
onClick: () => {

}
}
},
{
$$typeof: Symbol(react.element),
type: ‘span’,
key: “2”,
props: {
children: 0
}
}]
可以看到 React 将属性 $$typeof 添加到这些对象中,作为 React 元素的唯一标识。
而且有 type 、 key 、和 props 这些属性对 React 元素举行描述。
它们的值来源于传递给 react.createElement 函数的参数。
请关注 React 是如何将文本内容表示为 span 和 button 的子节点的。
以及如何把 click 的 handler 描述为 button 里 props 的一部分。
React 元素还有其他的很多字段,比如 ref ,但是超出本文范围不作展开。
ClickCounter 对应的 React 元素不存在任何 props 或 key :
{
$$typeof: Symbol(react.element),
key: null,
props: {},
ref: null,
type: ClickCounter
}
Fiber nodes

调和(reconciliation) 过程中,从 render 方法返回的每个 React element 的数据将被合并到 Fiber 节点树中,每个 React element 都有一个对应的 Fiber 节点。
与 React 元素差别, Fiber 并不是每次渲染都会重新创建,它们是用来生存组件 state 和 DOM 的可变数据布局。
之前聊到过,框架执行的活动,取决于 React 元素的范例。
在我们的示例中,对于类组件 ClickCounter 而言,它调用生命周期方法和 render 方法。
而对于 span host 组件(dom节点),执行 DOM 更新。
因此,每个 React 元素都被转换成 相应范例[11]的 Fiber 节点,描述必要完成的工作。
[译者注:这里的范例是 WorkTag 每个范例是一个固定的数字,比方函数式组件对应的是 0 而类组件对应的是 1]
你可以将 Fiber 想象成一种数据布局,用来表示一些要做的工作,或者换句话说,一个工作单位。 Fiber 的架构还提供了一种方便的方式来跟踪、调理、停息和中止工作。
当 React 元素第一次转换为 fiber 节点时,React在 createFiberFromTypeAndProps 函数中使用元素的数据来创建一个 Fiber 。
在后续更新中,React 复用了fiber节点,这意味着它只会根据数据发生改变的部分来更新对应的 fiber 节点中的属性。
如果从 render 方法不再返回相应的 React 元素,React 大概还必要根据 key 属性来移动或删除层级布局中的 fiber 节点。
   深入 ChildReconciler[12] 函数,了解所有活动的列表以及 React 为现有 fiber 节点执行的相应函数。
  因为 React 为每个 React 元素创建一个 fiber 节点,而且我们已经有一个这些元素构成的树,以是我们将会得到一个fiber 节点树。这样的环境下,我们简朴的示例看起来就像这样:

所有 fiber 节点使用这些属性: child 、 sibling 和 return 通过链接列表的情势连接在一起。
如果你想知道更多关于为什么要这样的更多信息,可以阅读这篇文章 The how and why on React’s usage of linked list in Fiber[13] (如果你还没读过的话)
Current 树和 workInProgress 树

在第一次渲染之后,React 最终得到了一棵反映渲染出 UI 的应用步伐 state 的 fiber 树。
这棵树通常被称为 current 树。当 React 开始处理惩罚更新时,它会构建一棵所谓的 workInProgress 树,反映未来要刷新到屏幕的 state。
所有的 work 都是在 workInProgress 树的 fibler 上举行的。当 React 遍历 current 树时,它为每个现有的光纤节点创建一个替换节点。
这些节点构成了 workInProgress 树。它们用 render 方法返回的 React 元素的数据创建。
一旦处理惩罚完所有 update 并完成所有相干 work,React 将一棵准备好的备用树刷新到屏幕。
一旦在屏幕上渲染 workInProgress 树,它就成为了 current 树。
React 的焦点原则之一 consistency (同等性)。
React总是一次性更新 DOM (它不会表现部分结果)。 workInProgress 树作为用户看不到的 “草稿”,以便 React 可以在处理惩罚所有组件之后,再将它们的更新刷新到屏幕上。
在源代码中,你会看到许多函数从 current 树和 workInProgress 树中获取 fiber 节点。下面是一个这样的函数的示例:
function updateHostComponent(current, workInProgress, renderExpirationTime) {

}
每个 fiber 节点都在 alternate 字段中保留了在另一棵树上其对应节点的引用。 current 树中的一个节点指向 workInProgress 树中的节点,反之亦然。
Side-effects 副作用

我们可以将 React 中的组件视为使用 state 和 props 来计算 UI如何出现的函数。
除此之外的所有活动,比方,改变DOM 或调用生命周期方法,都应被视为 Side-effects ,或者简朴地视为一种 effect。
在 文档[14]里也有提及。
   你之前大概已经在 React 组件中执行过获取数据、订阅或者 手动修改 DOM。我们统一把这些操作称为 “Side-effects”,或者简称为 “effect”。(因为它们会影响其他组件,而且在渲染期间无法完成。) ”
  你可以看到大多数 state 和 props 的更新将如何导致 side-effects 。
而且,由于执行 effect 是一种 work,fiber 节点是一种跟踪更新效果的便捷机制。
每个fiber 节点都可以包罗与其相干的 effect,在 effectTag 字段中。
因此,Fiber中的 effect 基本上界说了实例在处理惩罚更新后必要完成的 work[15]:
•对于 host 组件(dom元素),包罗添加、更新或删除元素。•对于类组件,React 大概必要更新 refs 并调用 componentDidMount 和 componentDiddUpdate 生命周期方法。•还有其他 effect 对应于其他范例的 fiber。
Effects 链表

React 执行 update 非常快,它采用了一些有趣的技术来达到这种性能水平:
创建具有 effect 的 fiber 节点的线性链表以实现快速迭代是其中之一
迭代线性链表比树快得多,不必要花时间在没有 side-effects 的节点上。
该链表的目的是,标记具有 DOM 更新或与其他 effect 关联的的节点。
它是 finishedWork 树的子集,而且使用 nextEffect 属性而不是 current 树和 workInProgress 树中的 child 属性举行链接。
Dan Abramov[16] 提出了一个 effect 链表的类比,把它想象成一棵圣诞树,"圣诞灯"把所有有效的节点绑在一起。
为了将其可视化,让我们想象下面的 fiber 节点树:
其中,高亮的节点有一些 work 要做,比方,我们的更新导致 c2 插入到 DOM 中。
d2 和 c1 更改属性, B2 触发生命周期方法。
effect 链表将它们链接在一起,以便 React 可以稍后跳过其他节点:

可以看到,具有 effect 的节点是如何链接在一起的。
当遍历节点时,React 使用 firstEffect 指针来确定列表的起始位置。以是上面的图表可以表示为这样的线性链表:

Fiber 树的根节点

每个 React 应用步伐都有一个或多个充当容器 DOM 元素。在我们的例子中它是 ID 为 container 的 div 。
const domContainer = document.querySelector(‘#container’);
ReactDOM.render(React.createElement(ClickCounter), domContainer);
React为每个容器创建一个 fiber root [17] 对象。你可以使用 DOM 元素的引用来访问它:
const fiberRoot = query(‘#container’)._reactRootContainer._internalRoot
这个 fiber root 是 React 生存对 fiber 树的引用的地方。存储在 fiber root 的 currrent 属性中:
const hostRootFiberNode = fiberRoot.current
Fiber 树以 特别范例[18] 的 fiber 节点 HostRoot 开始。
它是在内部创建的,并充当最顶层组件的父级。
HostRoot fiber 节点通过 stateNode 属性链接到 FiberRoot :
fiberRoot.current.stateNode === fiberRoot; // true
你可以通过 fiber root 访问最顶层的 HostRoot fiber 节点来探索 fiber tree。
或者,你可以从组件实例中获取单个 fiber 节点,像这样:
compInstance._reactInternalFiber
Fiber 节点布局

来看看为 ClickCounter 组件创建的 fiber 节点的布局:
{
stateNode: new ClickCounter,
type: ClickCounter,
alternate: null,
key: null,
updateQueue: null,
memoizedState: {
count: 0
},
pendingProps: {},
memoizedProps: {},
tag: 1,
effectTag: 0,
nextEffect: null
}
以及 span DOM 元素:
{
stateNode: new HTMLSpanElement,
type: “span”,
alternate: null,
key: “2”,
updateQueue: null,
memoizedState: null,
pendingProps: {
children: 0
},
memoizedProps: {
children: 0
},
tag: 5,
effectTag: 0,
nextEffect: null
}
fiber 节点上有很多字段。在前面已经描述过字段 alternate 、 effectTag 和 nextEfect 的用途。现在看看其他的字段的用途。
stateNode

生存对类组件实例,DOM 节点或与 fiber 节点关联的其他 React 元素范例的引用。一样平常来说,此属性用于生存与 fiber 关联的 local state。
type

界说与此 fiber 关联的函数或类。对于类组件,它指向构造函数,对于 DOM 元素,它指定 HTML 标记。
我把这个字段理解为 fiber 节点与哪些元素相干。
tag

界说 fiber范例[19],在 reconciliation 算法中使用它来确定必要完成的工作。
就像前面提到的,work 取决于 React 元素的范例。 createFiberFromTypeAndProps函数 [20]将 React 元素映射到相应的 fiber 节点范例。
在我们的应用中, ClickCounter 组件的 tag 属性是 1 ,它表示 类组件 ;
而 span 元素的 tag 属性是 5 表示 HostComponent(宿主组件) 。
updateQueue

state 更新,回调以及 DOM 更新的队列。
memoizedState

用于创建输出的 fiber 的状态。处理惩罚更新时,它反映了当前渲染在屏幕上的内容的 state。
memoizedProps

在上一次渲染期间用于创建输出的 fiber 的 props 。
pendingProps

在 React element 的新数据中更新而且必要应用于子组件或 DOM 元素的 props。(子组件或者 DOM 中将要改变的 props)
key

唯一标识符,当具有一组 children 的时间,用来资助 React 找出哪些项已更改,已添加或已从列表中删除。与这里[21]所说的React的 “列表和key” 功能有关
你可以在这里[22]找到 fiber节点的完整布局。在上面的阐明中,省略了很多字段。
尤其是跳过了构成树布局的 child 指针, sibling 指针和 return 指针。
[这些布局我在 上一篇文章 [23]中有所描述。]
以及专门针对 Scheduler 的 expirationTime , childExpirationTime 和 mode 等字段种别。
通用算法

React 执行工作分为两个重要阶段:render 和 commit
在 render 阶段,React 通过 setState 或 React.render 有筹划的将更新应用于组件,并找出必要在 UI 中更新的内容。
如果是初始渲染,React 会为 render 方法返回的每个元素创建一个新的 fiber 节点。在后续更新中,现有 React 元素的 fiber 节点将被复用和更新。
**该阶段的执行的结果是带有 effect 的 fiber 节点树。**effect 描述了在下一个 commit 阶段必要完成的工作。
当前阶段,React 会绘制一棵标记有 effect 的 fiber 树,并将其应用于实例。它遍历 effect 链表并执行 DOM 更新和用户可见的其他更改。
理解 render 阶段的工作可以异步执行,对我们而言非常重要。React 可以根据可用时间来处理惩罚一个或多个 fiber 节点,然后中断而且暂存已完成的工作,转去处理惩罚某些变乱,接着它再从它制止的地方继续执行。
但有时间,它大概必要丢弃完成的工作然后重新从头开始。
由于在此阶段执行的工作不会导致任何用户可见的更改(比方DOM更新),以是才可以实现这些停息。
(译者注:因为 React 的同等性,以是不大概给用户出现渲染到一半的组件,这样意味着这个阶段执行的所有 work 都是用户不可见的。)
与之相反的是,后续的 commit 阶段始终是同步的。这是因为在此阶段执行的工作会导致用户可见的更改,比方 DOM 更新。这就是为什么 React 必要一次性完成这些操作。
调用生命周期方法是 React 的工作之一。一些方法是在 render 阶段调用的,而另一些方法则是在 commit 阶段调用。
这是在第一个 render 阶段工作时,调用的生命周期列表:
•[UNSAFE_]componentWillMount (deprecated)•[UNSAFE_]componentWillReceiveProps (deprecated)•getDerivedStateFromProps•shouldComponentUpdate•[UNSAFE_]componentWillUpdate (deprecated)•render
如你所见,在16.3版本中,在 render 阶段执行的一些遗留的生命周期方法被标记为 UNSAFE 。
(译者注:这里的 “unsafe” 不是指安全性,而是表示使用这些生命周期的代码在 React 的未来版本中更有大概出现 bug,尤其是在启用异步渲染之后。参考官方文档[24])
现在在文档中它们被称为遗留 (legacy lifecycles) 生命周期。将在以后的 16.x 发行版中弃用,而没有 UNSAFE 前缀的对应版本将在 17.0版本中移除。
你可以在这里[25]具体的了解这些更改,以及发起的迁徙路径。
你是否对此感到好奇?
好吧,我们刚刚了解到,由于 render 阶段不会产生诸如 DOM 更新之类的 effect,因此 React 可以异步处理惩罚组件的异步更新(甚至大概在多个线程中举行)。
但是,标有 UNSAFE 的生命周期常常被误解和奇妙地滥用。开发人员倾向于将带有 effect 的代码放在这些方法中,这大概会导致新的异步渲染方法出现问题。
Although only their counterparts without the UNSAFE prefix will be removed, they are still likely to cause issues in the upcoming Concurrent Mode (which you can opt out of).
(译者注:这一段并不是忘记翻译,我对作者描述的内容还有疑问,已经在沟通中,后续会同步到 frontendwingman 的章节中)
接下来摆列的生命周期方法是在第二个 commit 阶段执行的:
•getSnapshotBeforeUpdate•componentDidMount•componentDidUpdate•componentWillUnmount
因为这些方法都在同步的 commit 阶段执行,他们大概会包罗 side-effects ,而且操作DOM。
好的,现在我们拥有了背景之后,让我们继续深入研究,用于遍历树和执行 work 的通用算法 。
Render 阶段

调和算法始终使 renderRoot[26] 函数从最顶层的 HostRoot fiber 节点开始。但是,React 会退出(跳过)已经处理惩罚的 fiber 节点,直到找到工作未完成的节点。
比方,如果你在组件树的深处调用 setState ,React将从顶部开始,但会快速跳过父级,直到它到达调用了 setState 方法的组件。
WorkLoop 的重要步骤

所有的 fiber 节点都会在 work loop[27]. 中举行处理惩罚。如下是该循环的同步部分的实现:
function workLoop(isYieldy) {
if (!isYieldy) {
while (nextUnitOfWork !== null) {
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
} else {

}
}
在上面的代码中, nextUnitOfWork 持有对 workInProgress 树中 fiber 节点的引用。
该节点必要完成一些工作。当 React 遍历 Fibers 树时,它通过此变量来判断是否还有其他未完成的 fiber 节点。
处理惩罚完当前光纤后,该变量将包罗对树中下一个光纤节点的引用或为“ null”。在这种环境下,React退出工作循环并准备提交更改。
处理惩罚过当前 fiber 后,变量将持有树中下一个 fiber 节点的引用或 null 。在为 null 的环境下,React 退出工作循环并准备好提交更改。
有四个重要函数用于遍历树并初始化或完成工作:
•performUnitOfWork[28]•beginWork[29]•completeUnitOfWork[30]•completeWork[31]
为了演示他们的使用方法,请看以下遍历 fiber 树的动画。我演示中使用了这些函数的简化实现。
每个函数都必要对一个 fiber 节点举行处理惩罚,当 React 顺着树往下遍历时,当前活动的 fiber 节点发生了变化。
图中可以清楚地看到,算法是如何从一个分支转到另一个分支。
在回溯到父节点之前,它首先完成子节点的 work,。

   留意,垂直方向连线表示同级(sibling 兄弟节点),而弯曲的连接表示子级,比方 b1 没有孩子,而 b2 有一个孩子 c1 。
     (译者注,图中的树布局,按照正常的从顶到根的次序分列的话,应该是从左往右看)
  这里 [32]是视频的链接,你可以停息播放并检查当前节点和函数状态。
从概念上讲,你可以将 “开始 (begin)” 视为 “进入 (stepping into)” 组件,而将“完成 (complete)” 视为 “脱离 (stepping out)” 组件。
当我解释这些功能时,你也可以在 这里[33] 体验示例和实现。
我们从 performUnitOfWork 和 beginWork 开始:
function performUnitOfWork(workInProgress) {
最后

根本知识是前端一面必问的,如果你在根本知识这一块翻车了,就算你框架玩的再6,webpack、git、node学习的再好也无济于事,因为对方就不会再给你展示的机遇,万万不要因为根本错过了自己心怡的公司。前端的根本知识杂且多,并不是理解就ok了,有些是真的要去记。当然了我们是牛x的前端工程师,每天像背英语单词一样去背知识点就没须要了,只要平时工作中多留意总结,口试前端刷下题目就可以了。
开源分享:【大厂前端口试题分析+焦点总结学习笔记+真实项目实战+最新解说视频】
转到另一个分支。
在回溯到父节点之前,它首先完成子节点的 work,。

   留意,垂直方向连线表示同级(sibling 兄弟节点),而弯曲的连接表示子级,比方 b1 没有孩子,而 b2 有一个孩子 c1 。
     (译者注,图中的树布局,按照正常的从顶到根的次序分列的话,应该是从左往右看)
  这里 [32]是视频的链接,你可以停息播放并检查当前节点和函数状态。
从概念上讲,你可以将 “开始 (begin)” 视为 “进入 (stepping into)” 组件,而将“完成 (complete)” 视为 “脱离 (stepping out)” 组件。
当我解释这些功能时,你也可以在 这里[33] 体验示例和实现。
我们从 performUnitOfWork 和 beginWork 开始:
function performUnitOfWork(workInProgress) {
最后

根本知识是前端一面必问的,如果你在根本知识这一块翻车了,就算你框架玩的再6,webpack、git、node学习的再好也无济于事,因为对方就不会再给你展示的机遇,万万不要因为根本错过了自己心怡的公司。前端的根本知识杂且多,并不是理解就ok了,有些是真的要去记。当然了我们是牛x的前端工程师,每天像背英语单词一样去背知识点就没须要了,只要平时工作中多留意总结,口试前端刷下题目就可以了。
开源分享:【大厂前端口试题分析+焦点总结学习笔记+真实项目实战+最新解说视频】

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

民工心事

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表