第一周----整理知识点(浏览器 & VNode & vue-diff算法 2.0与3.0的区别)

170 阅读15分钟

浏览器相关

在浏览器里输入一行url发生了哪些事情

  • 网络请求

    • 构造请求

    假如你输入了www.baidu.com/ 那么浏览器会构造一个请求行

    // 请求方法是Get方法
    // 请求路径是根路径
    // 请求协议版本号是1.1
    Get / HTTP1.1
    
    • 查找强缓存

    如果找到强缓存就直接使用,否则往下走

    • DNS解析

    通过域名找到对应的IP地址的过程就叫做DNS解析。注意:浏览器有DN解析缓存机制,当一个域名已经被解析过,下次直接使用解析过得IP不会再做DNS解析了

    • 建立TCP连接

    TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。

    TCP的链接经历了以下三个过程

    1. 三次握手

    解释: A主机为客户端 B主机为服务端

    1. A->B 我发送序号200,想要跟你建立连接,你能收到吗?作用:客户端的发送没问题

    2. B->A 我收到啦,你发送的序号是200,我在此基础上给你加一,确认序号是201,我的序号是500。 作用:服务端接收没有问题,发送没有问题

    3. A->B 我收到啦,你发送的序号是500,,我在此基础上给你加一,确认序号是501,我的序号需要执行加一,是201。作用:客户端接收没有问题,发送没有问题 整个流程下来可以确保客户端,服务器发送接收都没有问题

    4. 数据传输

    发送方向接收方发送数据,如果接收方没有接受到则认为是数据包丢失,发送方会重新发送该数据包,直到接收方接收到之后给个确认消息ACK=1;另外,数据包的传输可以使用优化策略,可以把数据包拆分成一个一个的小包,接受方接收到之后可以按照顺序组合成完整的数据包

    1. 四次挥手

    断开数据连接,客户端和服务器均可主动断开 解释:

    1. A->B 我发送序号200,想要跟你断开连接,你能收到吗?A主动关闭TCP连接,进入FIN_WAIT1(终止等待1)状态
    2. B->A 我收到啦,你发送的序号是200,我在此基础上给你加一,确认序号是201,我的序号是500。但是我不能立刻断开,因为我还有后续的报文没有执行完,等我执行完所有的保温之后再给你发一个消息B收到断开连接消息之后,进入CLOSE_WAIT(等待关闭)状态,此时的TCP处于半关闭状态
    3. B->A 我所有的报文已经执行完了,我要跟你断开连接, 我的序号是501,你发送的序号是200,我在此基础上给你加一,确认序号是201,你能收到吗?B处于LAST_ACK (最后确认)的状态,只需等待A反馈即可
    4. A->B 我收到啦,你的序号是501,再次基础上我给你加一,确认序号是502,我的序号是201。B收到消息就处于CLOSED (关闭连接)的状态,而A还需要等待计时器设置的时间2MSL后,确保B一定收到消息后才能处于CLOSED (关闭连接)的状态。问: 为什么是2MSL等待时间?

    思考问题

    问为什么要三次握手 两次不行吗?答:只有三次才能确保客户端服务端发送和接收功能都正常

    问第三次握手失败了怎么办?答:第一次握手成功后,服务器就会把这种请求放在半连接队列里面,如果第三次握手失败,服务器会重发消息给客户端,要是还收不到,再重发,直到超出系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。

    问:三次握手中可以携带数据吗?答:第一次第二次不能携带,第三次可以携带,因为已经建立链接了,第一次第二次分别是判断客户端发送,服务端接受,发送是否有问题,如果再此期间发送大量数据,可能会使服务器遭受到攻击,比如黑客故意在一次发送报文中携带大量数据,就会导致真实的客户端的数据不能被服务器接收,一直如此的话,此连接会被系统删除或者引起网络瘫痪。

    问挥手为什么要四次?答:因为服务端可能还有排队中的报文没有执行完毕,需要等所有报文执行完毕之后再次给客户端发起断开通知

    问等待时间为什么是2MSL?答:报文段在传输过程中最大的生存时间是MSL,2倍的MSL可让TCP再次发送最后的ACK以防这个ACK丢失(另一端超时并重发最后的FIN)。并且再此时间段期间,再次调取该接口,则不能被使用,只有过了2SML之后才能被再次使用

    • 发送 http请求

    现在TCP建立连接之后 浏览器就可以跟服务器通信啦,浏览器发送http请求需要携带三样东西,请求行 请求头 请求体

    1. 请求行

    第一步就已经构建好了

    // 请求方法是Get方法
    // 请求路径是根路径
    // 请求协议版本号是1.1
    Get / HTTP1.1
    
    1. 请求头

    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
    Accept-Encoding: gzip, deflate, br
    Accept-Language: zh-CN,zh;q=0.9
    Connection: keep-alive
    Cookie: __yjs_duid=1_38ab900cd127edf5d094be0e7ad1ca6c1613974406843; PSTM=1614133943; BAIDUID=3E42FD376379681FD056171C116AF3A2:FG=1; BD_HOME=1; BIDUPSID=2BFFAC236F132502F30267E697F3F002; BD_UPN=123253; delPer=0; BD_CK_SAM=1; BAIDUID_BFESS=3E42FD376379681FD056171C116AF3A2:FG=1; H_PS_PSSID=33514_33344_33570_33603_33459_26350_33265; plus_lsv=44d0b5b8089055d9; plus_cv=1::m:7.94e+147; Hm_lvt_12423ecbc0e2ca965d84259063d35238=1614395292; SE_LAUNCH=5%3A26906588; H_WISE_SIDS=107318_110085_127969_131424_144966_155930_156286_156927_162898_163569_164107_164326_164456_164955_165134_165135_165329_166148_166184_166830_167069_167085_167114_167296_168388_168489_168494_168501_168542_168763_168972_169055_169063_169164_169307_169373_169699_169806_169876_170251_8000056; rsv_i=037f%2BAjO4xwfT6R%2B0q276fCmRYZpd9%2FIJUTBSDgD89kodMI%2F3h3OdLRwqsuqIjmhUBXTnwrIk2ihcNBzwsHDM3N%2Bm%2F9Llr0; Hm_lpvt_12423ecbc0e2ca965d84259063d35238=1614395308; PSINO=2; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; BDSVRTM=0; COOKIE_SESSION=234663_9_7_0_35_57_0_7_0_7_3_0_234668_0_3_2_1614396170_1609061618_1614396167%7C9%2321164_7_1609061616%7C4
    Host: www.baidu.com
    Sec-Fetch-Dest: document
    Sec-Fetch-Mode: navigate
    Sec-Fetch-Site: none
    Sec-Fetch-User: ?1
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1
    
    1. 请求体

    像一个接口传的参数

    [{"index":1,"pageSize":50,"projectCode":"jingxiaocang","contentType":2,"resCode":"pos00007"}]
    
    • 网络响应

    由三部分组成:响应行,响应头,响应体

    1. 响应行

     Request URL: https://www.baidu.com/
     Request Method: GET
     Status Code: 200 OK
     Remote Address: 110.242.68.3:443
     Referrer Policy: strict-origin-when-cross-origin
    
    1. 响应头

     Cache-Control: no-cache
    	Connection: keep-alive
    	Content-Encoding: gzip
    	Content-Type: text/html;charset=utf-8
     Coremonitorno: 0
     Date: Sat, 27 Feb 2021 04:47:52 GMT
     Server: apache
     Set-Cookie: H_WISE_SIDS=107318_110085_127969_131424_144966_155930_156286_156927_162898_163569_164107_164326_164456_164955_165134_165135_165329_166148_166184_166830_167069_167085_167114_167296_168388_168489_168494_168501_168542_168763_168972_169055_169063_169164_169307_169373_169699_169806_169876_170251_8000056; path=/; expires=Sun, 27-Feb-22 04:47:52 GMT; domain=.baidu.com
     Set-Cookie: bd_traffictrace=271247; expires=Thu, 08-Jan-1970 00:00:00 GMT
     Set-Cookie: rsv_i=8c5dx20kTSWp85qcyacEdS0KjAHRn7Xz2T3xBpRnALGftAoTTxv8K2BKvie6RZVYZri94ft%2FcAHAP9sZUYXRnPzuqm5sdBE; path=/; domain=.baidu.com
     Set-Cookie: BDSVRTM=466; path=/
     Set-Cookie: eqid=deleted; path=/; domain=.baidu.com; expires=Thu, 01 Jan 1970 00:00:00 GMT
     Set-Cookie: __bsi=; max-age=3600; domain=m.baidu.com; path=/
     Strict-Transport-Security: max-age=172800
     Traceid: 161440127224717007467137245922687506809
     Transfer-Encoding: chunked
     Vary: Accept-Encoding
    
    1. 响应体

    {"code":0,"data"[{"confKey":"xiangqinggonggao","confValue":""}],"msg":"调用成功"}
    

    问响应完成之后,TCP就断开连接了吗?答:不一定 ,如果请求头或响应头中包含Connection: Keep-Alive,表示建立了持久连接,这样TCP连接会一直保持,之后请求统一站点的资源会复用这个连接,如果没有该字段则断开TCP连接, 请求-响应流程结束。

    总结:

  • 页面渲染

    • 构建dom树

      • 标记树 根据元素解析比如 <div>hahsh</div>
      1. 当解析到< 状态为标记打开,
      2. 当解析到a-z字符时状态为标记名称状态
      3. 当解析到> 状态为标记名称状态结束,这时候变为数据状态
      4. 知道解析到/这时候会创建一个 end tag的标识 随后进入标记名称状态。
      • 建树 DOM树是一个以document为根节点的多叉树,因此解析器会首先创建一个document,接着标记器会把标记的信息给到建树器,建树器接收到信息之后开始创建对应的DOM对象,创建这个对象之后会做两件事
      1. 将Dom对象添加到DOM树上
      2. 将标签闭合元素压入栈中;直到标记生成器最后传过来一个html的结束标记,结束解析
    • 样式计算

    样式来源

    1. link引入
    2. style
    3. class

    格式化样式

    浏览器是无法直接识别css样式的,因此渲染引擎收到css文本时,首先生成一个样式对象 styleSheet,可以通过document.styleSheets查看该结构 ![]!

    标准化样式

    根据css里面的 white->#ffffff black->#000000 blod->700 等

    计算每个元素的样式

    规则:继承+层叠 继承父元素样式,叠加自己的样式,最终组合出现的样式

    • 生成布局树

    有DOM,有样式,但是还要确定元素在浏览器里面的位置,所以需要生成布局树。

    1. 遍历DOM树里面的节点,添加到布局树里面

    2. 给每个元素确定位置 注意:布局树只有包含可见的元素,head和display:none不存在与布局树里

    • 构建图层树

    因为会有z-index position scroll 等,所以需要构建一个图层树,

    1. 显示合成

      • 设置定位 position不为static的定位 并且设置了z-index属性
      • 设置滤镜 filter不为none时
      • 设置伸缩 transfrom不为none时
      • 设置opticy不为1时
      • 设置新的层叠上下文时isolation
      • will-change 提前告知浏览器需要对元素做一些优化
      • 裁剪:如果超出块的高度或者宽度 生成滚动条时
    2. 隐式合成 层级低的元素被提升层级高度之后,所有比它高的都需要提升,如果元素过多的话,会造成GPU一下子压力变大,直接让页面崩溃,这就是层爆炸。

    • 生成绘制列表

    渲染器会将图层的绘制,生成一个待执行的绘制列表,比如先化边框还是先画背景,为后续的绘制做准备

    • 生成图块和位图

    绘制列表准备好之后 渲染进程的主线程会给合成线程发送消息,视图就那么大,一下子给太多的让我绘制,非常浪费性能,所以合成线程先将图层分块,通常是256256 512512这个规格,可以让绘制效率大大提升。 优化:因为图块也是会进入GPU,考虑到浏览器内存上传GPU还是会慢,所以chrome团队先上传低分辨率的图块,等到正常的图块上传完成再替换低分辨率的图块,这样就加快了首屏的渲染。 合成线程会把图层附近的图块交给栅格化线程,让它生成位图,生成的位图最后发给合成线程,合成线程会生成一个绘制指令,给到浏览器进程。

    • 显示器展示

    浏览器进程接收到绘制指令,把页面内容绘制存放在浏览器内存里,也就是页面,发送给显卡,显示器显示。 以上浏览器的绘制就结束了

渲染拓展

  • 重绘和重排是什么

根据上面的流程我们知道了整个浏览器的渲染流程,也知道渲染的主流程为哪些。

  • 重绘 重绘 顾名思义就是重新绘制样式,不涉及到dom元素的改变和几何元素(宽高,位置) 所以流程上只会改动样式计算和绘制列表
  • 重排/回流
    • 能造成重排的属性
    1. 盒子模型相关: width、height、padding、border、margin、display、border-width、min-height

    2. 定位属性及浮动: top、right、bottom、left、position、float、clear

    3. 节点内部文字结构,行内属性 font-size、line-height

    • 能造成重排的操作
    1. 读写位置属性时,比如 offset/scroll/client 等属性时会触发回流。从生成布局树之后全部走一遍
    2. 调用 window.getComputedStyle 方法从样式计算之后全部走一遍
    3. DOM 元素的几何属性(width/height/padding/margin/border)发生变化时会触发回流 从样式计算之后全部走一遍
    4. DOM 元素移动、删除、增加会触发回流。从头开始渲染
  • 从实际工作触发 了解这些之后 有什么感想

  1. 减少样式操作 + 尽量使用class改变样式 而不是通过js获取样式 一个一个改,减少回流与重绘的次数
  2. 减少DOM操作
  • 插入元素尽量使用createDocumentFragment文档片段,因为文档片段是存在内存的,不会涉及到回流。
  • 修改元素时要先克隆一下元素等操作好之后再次删除原有节点,插入即可。一次操作。
  1. 避免频繁直接访问计算后的样式,而是先将信息保存下来。一下代码不可取。
  2. 新建图层:将频繁重绘回流的dom元素单独作为一个独立图层,因此缩小了重绘和回流的影响范围。
  3. html不要嵌套太深, 否则会加大对页面布局的计算消耗
  4. 绝对布局的DOM, 不会造成大量回流
for(var i = 0;i<=10;i++){
var body = document.getElementsByTagName('body')[0]
var h = body.offsetHeight;}

VNode 虚拟Dom

  • 什么是虚拟dom

一个对象,对象里面有tag标识,childern

  • 虚拟Dom的出现有哪些好处

可以实现跨端,改变元素的数据可以通过数据劫持,而不用去跟jquery一样先找到元素再去修改元素 再去设置元素。

vue-Diff

  • vue2.0

头头比较 nodeStart++,newNodeStart++ 尾尾比较 nodeEnd--,newNodeEnd-- 头尾比较 尾头比较

  • vue3.0

从头开始比较,找到不相同的跳出 i++;再从尾开始比较 e1--,e2--,中间的改变 根据最长递增子序列移动位置 参考文献:

juejin.cn/post/684490… zhuanlan.zhihu.com/p/86426969