1、React Fiber的作用和原理
2、剑指前端offer
1、什么是 Fiber
1.1、React 15 的问题
React15的时候,在页面元素很多,且需要频繁刷新的场景下,React15会出现掉帧的现象。
其根本原因,是大量的同步计算任务阻塞了浏览器的 UI 渲染。
默认情况下,JS运算、页面布局和页面绘制都是运行在浏览器的主线程当中,他们之间是互斥的关系
如果 JS 运算持续占用主线程,页面就没法得到及时的更新。
当我们调用setState更新页面的时候,React 会遍历应用的所有节点,计算出差异,然后再更新 UI。
整个过程是一气呵成,不能被打断的。
如果页面元素很多,整个过程占用的时机就可能超过 16 毫秒,就容易出现掉帧的现象。
1.2、解题思路
解决主线程长时间被 JS 运算占用这一问题的基本思路,是将运算切割为多个步骤,分批完成。
也就是说在完成一部分任务之后,将控制权交回给浏览器,让浏览器有时间进行页面的渲染。
等浏览器忙完之后,再继续之前未完成的任务。
1.3、React 的答卷
React 框架内部的运作可以分为 3 层:
Virtual DOM 层,描述页面长什么样。
Reconciler 层,负责调用组件生命周期方法,进行 Diff 运算等。
Renderer 层,根据不同的平台,渲染出相应的页面,比较常见的是 ReactDOM 和 ReactNative。
这次改动最大的当属 Reconciler 层了,React 团队也给它起了个新的名字,叫Fiber Reconciler。这就引入另一个关键词:Fiber。
Fiber 其实指的是一种数据结构,它可以用一个纯 JS 对象来表示:
const fiber = {
stateNode, // 节点实例
child, // 子节点
sibling, // 兄弟节点
return, // 父节点
}
为了加以区分,以前的 Reconciler 被命名为Stack Reconciler。Stack Reconciler 运作的过程是不能被打断的,必须一条道走到黑
而 Fiber Reconciler 每执行一段时间,都会将控制权交回给浏览器,可以分段执行
波峰图
为了达到这种效果,就需要有一个调度器 (Scheduler) 来进行任务分配。任务的优先级有六种:
● synchronous,与之前的Stack Reconciler操作一样,同步执行
● task,在next tick之前执行
● animation,下一帧之前执行
● high,在不久的将来立即执行
● low,稍微延迟执行也没关系
● offscreen,下一次render时或scroll时才执行
优先级高的任务(如键盘输入)可以打断优先级低的任务(如Diff)的执行,从而更快的生效。
Fiber Reconciler 在执行过程中,会分为 2 个阶段
● 阶段一,生成 Fiber 树,得出需要更新的节点信息。这一步是一个渐进的过程,可以被打断。
● 阶段二,将需要更新的节点一次过批量更新,这个过程不能被打断。
阶段一可被打断的特性,让优先级更高的任务先执行,从框架层面大大降低了页面掉帧的概率
1.4、Fiber 树
Fiber Reconciler 在阶段一进行 Diff 计算的时候,会生成一棵 Fiber 树。
这棵树是在 Virtual DOM 树的基础上增加额外的信息来生成的,
它本质来说是一个链表。
Fiber 树在首次渲染的时候会一次过生成。
在后续需要 Diff 的时候,会根据已有树和最新 Virtual DOM 的信息,生成一棵新的树。
这颗新树每生成一个新的节点,都会将控制权交回给主线程,去检查有没有优先级更高的任务需要执行。
如果没有,则继续构建树的过程:
如果过程中有优先级更高的任务需要进行,则 Fiber Reconciler 会丢弃正在生成的树,在空闲的时候再重新执行一遍。
在构造 Fiber 树的过程中,Fiber Reconciler 会将需要更新的节点信息保存在Effect List当中,在阶段二执行的时候,会批量更新相应的节点。
1.5、总结 (递归改循环)
本文从 React 15 存在的问题出发,介绍 React Fiber 解决问题的思路,并介绍了 Fiber Reconciler 的工作流程。从Stack Reconciler到Fiber Reconciler,源码层面其实就是干了一件递归改循环的事情,
2、Diff 算法
react为什么不推荐使用index作为key
React diff 算法的底层原理
React之Diff算法
虚拟 DOM 到底是什么,说简单点,就是一个普通的 JavaScript 对象,包含了 tag、props、children 三个属性。
渲染虚拟 DOM
自定义一个render函数将虚拟DOM渲染成真实DOM后,只需要插入到对应的根节点即可。
第一次render在执行的时候会将第一次的虚拟dom做一次缓存,第二次渲染的时候会将新的虚拟dom和老的虚拟dom进行对比。
这个对比的过程其实就是diff算法。
React中的diff算法
1、层级对比, 当发现节点已经不存在,则该节点及其子节点会被完全删除掉
2、组件对比
如果是同一类型的组件,按照原策略继续比较 virtual DOM tree。
如果不是,替换整个组件下的所有子节点。
3、节点对比
三种方法:插入,移动,删除
对于同一层级的一组子节点,通过分配唯一唯一key值进行区分
Diff 算法进化 深度优先遍历 -> 两端同时进行比较的算法
React Hooks 实现原理
轻松学会 React 钩子:以 useEffect() 为例
3、 React Hooks
Hooks 主要是利用闭包来保存状态,使用链表保存一系列 Hooks,将链表中的第一个 Hook 与 Fiber 关联。在 Fiber 树更新时,就能从 Hooks 中计算出最终输出的状态和执行相关的副作用。
简单总结一下hooks:
它可以让我们在不编写class的情况下使用state以及其他的React特性;
Hook 就是 js 函数,
这个函数可以帮助你 钩入(hook into) React State以及生命周期等特性;
一句话,钩子(hook),用来为函数组件引入副效应。
为什么用hooks,
之前生命周期太多,业务的划分更清晰,
官方推荐使用钩子(函数),而不是类。因为钩子更简洁,代码量少,用起来比较"轻",而类比较"重"。而且,钩子是函数,更符合 React 函数式的本质。
怎么做,
??hooks 原理
回答关键点
闭包 Fiber 链表
Hooks 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
Hooks 主要是利用闭包来保存状态,使用链表保存一系列 Hooks,将链表中的第一个 Hook 与 Fiber 关联。在 Fiber 树更新时,就能从 Hooks 中计算出最终输出的状态和执行相关的副作用。
使用 Hooks 的注意事项:
不要在循环,条件或嵌套函数中调用 Hooks。
只在 React 函数中调用 Hooks。
4、 HTTP
HTTP/1.1 相较 HTTP/1.0 的改进和优化:
持久连接,默认开启 keep-alive
HTTP 管道化
断点续传、并行下载
分块编码传输
新增 Host 头处理
更多缓存处理
新增更多状态码
HTTP/1.1 的缺点:
1、队头阻塞,一个管道中同一时刻只能处理一个请求,所以如果上一个请求未完成,后续的请求都会被阻塞。
2、头部冗余,HTTP请求每次都会带上请求头,若此时cookie也携带大量数据时,就会使得请求头部变得臃肿。
3、TCP 连接数限制,
HTTP/2 的优点:
二进制分帧层,HTTP/1.x 中传输数据使用的是纯文本形式的报文, HTTP/2 则是采用二进制编码
多路复用,单个 TCP 连接上并行地处理多个请求和响应
Header 压缩
服务端推送,允许服务端主动向浏览器推送额外的资源,不再是完全被动地响应请求。
5、浏览器从输入网址到页面展示的过程
1.URL 输入
2.DNS 解析
3.建立 TCP 连接
4.发送 HTTP / HTTPS 请求(建立 TLS 连接)
5.服务器响应请求
6.浏览器解析渲染页面
7.HTTP 请求结束,断开 TCP 连接
1.判断URL是否完整, 合法的
2.DNS解析,就是找IP地址,本地DNS服务器(是否有缓存) -> DNS根服务器 -> .org域名服务器 -> hzfe.org 域名服务器
3.建立TCP连接,
客户端发SYN包 到服务器,等待服务器确认。
服务器收到 SYN 包,确认应答客户端的 SYN,同时自己也发送一个 SYN 包给客户端
客户端收到服务器的 SYN+ACK 包,向服务器发送确认包 ACK,此包发送完毕,客户端和服务器进入连接状态,完成三次握手。
4. TLS 协商
建立连接后就可以通过 HTTP 进行数据传输。
如果使用 HTTPS,会在 TCP 与 HTTP 之间多添加一层协议做加密及认证的服务。
HTTPS 使用 SSL 和 TLS 协议
5. 服务器响应
浏览器 -> 服务器的连接建立后,浏览器会发送一个初始的 HTTP GET 请求,请求目标通常是一个 HTML 文件。
服务器收到请求后,将发回一个 HTTP 响应报文,内容包括相关响应头和 HTML 正文。
5.1 状态码 404 , 500
6. 浏览器解析并绘制
1.处理 HTML 标记并构建 DOM 树。
2.处理 CSS 标记并构建 CSSOM 树。
3.将 DOM 与 CSSOM 合并成一个渲染树。
4.根据渲染树来布局,以计算每个节点的几何信息。
5.将各个节点绘制到屏幕上。
7. TCP 断开连接, 四次挥手
1.主动关闭方发送一个 FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不会再给你发数据了(在 FIN 包之前发送出去的数据,如果没有收到对应的 ACK 确认报文,主动关闭方依然会重发这些数据),但此时主动关闭方还可以接受数据。
2.被动关闭方收到 FIN 包后,发送一个 ACK 给对方,确认序号为收到序号+1(与 SYN 相同,一个 FIN 占用一个序号)。
3.被动关闭方发送一个 FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。
4.主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。
1、半双工是指传输过程中同时只能向一个方向传输.一方的数据传输结束之后,另外一方再回应。
2、全双工是指两方能同时发送和接受数据。
6、XSS CSRF
XSS-插入恶意的Script代码, 让用户的浏览器去执行。
攻击方式一:插入script脚本, 比如评论区么
攻击方式二:利用onError事件
http:.../xss/1.php?name=<img src='w.123' onerror='alert("hey!")'>
指定的图片不存在就会发生错误,这时候onerror里面的代码就执行了, 就弹窗了
危害:
盗取cookie,<script>document.cookie</script>
流量劫持
XSS 分类
反射性XSS(非持久型XSS)
存储型XSS(持久型XSS)
XSS 防御
首先是过滤。对诸如<script>、<img>、<a>等标签进行过滤。
其次是编码。像一些常见的符号,如<>在输入的时候要对其进行转换编码,这样做浏览器是不会对该标签进行解释执行的,同时也不影响显示效果。
设置Http-only禁止通过javaScript获取cookie。
CSRF
跨站请求伪造,是指黑客诱导用户打开黑客的网站,在这个网站中,利用用户的登陆状态发起的跨站请求。
CSRF攻击就是利用了用户的登陆状态,并通过第三方的站点来做一个坏事。
要完成一次CSRF攻击,受害者依次完成两个步骤:
1. 登录受信任网站A,并在本地生成Cookie
2. 在不登出A的情况,访问危险网站B
这就是为什么在服务端要判断请求的来源,及限制跨域。
如何防御CSRF
1、利用cookie的SameSite,SameSite有3个值:Strict, Lax和None
我们可以根据实际情况将一些关键的Cookie设置 Stirct或者 Lax模式,这样在跨站点请求的时候,这些关键的Cookie就不会被发送到服务器,从而使得CSRF攻击失败。
2、验证请求的来源点
3、CSRF Token
1. 最开始浏览器向服务器发起请求时,服务器生成一个CSRF Token。CSRF Token其实就是服务器生成的字符串,然后将该字符串种植到返回的页面中(可以通过Cookie)
2. 浏览器之后再发起请求的时候,需要带上页面中的 CSRF Token(在request中要带上之前获取到的Token,比如 x-csrf-token:xxxx), 然后服务器会验证该Token是否合法。第三方网站发出去的请求是无法获取到 CSRF Token的值的。
React 合成事件
https://rosefile.net/p5wrzw9ioe/788VTT23.zip.html
react合成事件,就是基于事件委托的
事件差异3个, 命名驼峰,函数名,阻止默认行为
16版本,绑定到document,事件池(可复用事件)在17版本废弃了,
react内部实现dispatchEvent
//注册React事件的事件委托
document.addEventListener('click', dispatchEvent)
17,绑定到root容器上,react17版本改变最大的就是事件
事件委托的对象不再是document,而是root,
event是合成事件对象 nativeEvent是它对应的原生事件对象
不再向上冒泡了,但是本元素剩下的监听函数还是会执行
event.nativeEvent.stopPropagation()
不但不向上冒泡了,本级剩下的监听函数也不执行
event.nativeEvent.stopImmediatePropagation()
网友评论