一、URL解析

主要就是判断你输入的是否是一个合法的URl、并且根据你输入的内容进行自动完成字符编码等操作!

二、DNS解析

服务器真实的地址一般是数字、或者数字加字母、如果我们去记数字或者字母的话未免太长了、所以我们一般会把域名和IP地址进行关联、我们只需要记录域名就好、比如bilibili.com, 所以我们如果需要拿到服务器资源的话,我们就需要对域名进行转换,得到服务器真实的IP地址, 而这个过程我们叫做DNS域名解析、DNS解析是一个递归的过程 在了解DNS解析的过程前、我们需要了解下域名的管理、比如bilibili.com其这些常见域名也是有人管理的那就是root(根域名服务器)但是因为都是属于root管理所以我们一般就不会去写了、所以总结 根域名服务器(共13域名) —-管理—-> 顶级域名服务器(com, cn, net, gov) —-管理—-> 权威域名服务器(bilibili, baidu, ali….)

image.png

浏览器DNS域名解析

  • 查询自己浏览器缓存中有没有对应的IP记录(有就直接拿到IP)
  • 主机查询下本地文件(hosts)里面有没有对应记录(有就直接拿到IP)
  • 如果都没有、浏览器会调用解析器(程序写的)向本地DNS服务器发送请求( 互联网服务提供商) 有些本地NDS服务器可能还会记录下你解析了什么域名,哈哈哈,莫非你看黄被记录着了? 、假设本地NDS服务器在本地没有任何根域名服务缓存的时候:

1、首先查询下根服务器域名、得到根服务器域名后在通过域名查询这个服务器IP,然后得到根域名IP、
2、通过根域名IP进行通讯查询通用顶级域名地址、这次查询会得到域名名称和对应的域名IP、
3、然后会选择一个顶级域名IP进行咨询我们bilibili.com权威域名服务器地址、这样我们就能得到bilibili.com权威域名服务器地址
4、然后我们在和这个权威域名服务器进行对话、然后向权威域名服务器咨询www.bilibili域名服务器的IPv4地址、得到地址后本地NDS解析器就会把真实的IP地址返回给我们的解析器!
这样NDS解析就完成了!下面我们模仿下DNS解析器过程、使用的是www.bilibili.com!

本地NDS解析过程

第一步查询根域名服务器和根域名服务器IP地址
  1. nslookup // 回车
  2. set type=ns //回车
  3. . //回车、得到一堆根域名服务器地址 Non-authoritative 是因为一般通过缓存得到的结果

image.png

  1. set type=a
  2. b.root-servers.net. //随便选一个根域名地址,回车得到对应的IP地址

image.png

第二步 和根域名IP进行对话查询顶级域名地址
  1. server 199.9.14.201
  2. set type=ns
  3. com. // 向根域名地址咨询.com域名地址、

image.png

这样我们就得到了一堆顶级域名地址和对应的IP地址了、然后又可以挑一个通过顶级域名查询bilibili下的域名IP地址

第三步 向顶级域名咨询bilibili.com的权威域名地址
  1. server 192.33.14.30
  2. set type=ns
  3. bilibili.com. //查询它的域名权威域名服务器

image.png

第四步 向权威域名服务咨询www.bilibili.com.域名服务器的IPv4地址
  1. server 162.14.18.188
  2. set type=a
  3. www.bilibili.com.

image.png

第五步、获取都浏览器解析器需要的真实地址结果
  1. g.w.bilicdn1.com.

image.png

直接输入这个地址得到最终的IP地址、然后本地DNS服务器得到这个地址后会返回给我们的解析器、当然还会把地址进行缓存起来

三、 发起TCP连接

浏览器拿到域名服务器的IP就可以建立通信获取资源进行页面渲染了、但是在进行通信之前他们需要进行TCP连接、目的就是在发送请求前看看服务器的接收和发送是否正常! 我们可以通过Wireshark 进行抓包查看!!

三次握手
  • 第一次(客户端给服务器发送TCP报文信息)
    • 客户端会在TCP报文里面开启SYN、然后添加Sequence序号给服务器
  • 第二次(服务端给客户端发送报文信息)
    • 服务器做出响应、会在TCP报文中把SYN和ACK开启、并且会携带自己的序号和确认号 ( 确认号是由客户端的序号进行+1)
  • 第三次(客户端给服务器发送报文信息)
    • 客户端会把ACK开启、然后会携带序号:这个序号是根据服务端给的序号+1

image.png

四、 发送HTTP请求

TCP连接验证客户端和服务端都有着接收和发送的功能后、客户端就会发送请求报文、服务端回送响应报文

image.png

第一步构建请求

浏览器会构建请求行信息请求包含三部分: 请求的方法、请求URL、HTTP协议版本

检查强缓存

在发送请求前呢、浏览器会进行缓存检测、因为你发送的请求有可能上次请求过的、并且是服务器告诉你需要强缓存的、所以第一步是需要先检查下缓存、首先会检查请求头是否携带了强缓存信息expires和cache-control (服务端设置的) 、如果有强缓存标识、那就直接读取缓存文件、不发送请求、命中强缓存后浏览器会优先去内存(memory cache)中查找、然后在考虑磁盘(disk cache) 如果都没有那就进行网络请求!

image.png

开始协商缓存

没有命中强缓存的话、开始发送请求到服务器、这个时候就会走协商缓存的逻辑、客户端会在请求头里面携带IF-Modified-since和If-None-Match字段信息、服务端会比较请求头的Etag如果相同则命中协商缓存(文件没有更改)、返回304状态、如果不一致有改动的话、直接返回新的资源文件带上新的Etag值并返回状态码200、

image.png

这里有个小知识、就是为什么webpack打包需要添加hash值的js、目的就是为了改变html资源路径地址、有更改后浏览器进入协商缓存的时候就会返回最新的内容给到客户端!

五、断开TCP连接

内容传输完后、各自就可能发起关闭连接要求了、这个过程我们叫四次挥手、客户端和服务端都能主动发起关闭请求!、假设这里是客户端发起的关闭请求

四次挥手
  • 第一次(客户端向服务端发送报文信息)
    • 客户端会在报文中开启FIN和ACK、携带序号和确认号
  • 第二次(服务端给客户端发送报文信息)
    • 服务端发送ACK、并携带序号和确认号、此时的序号是客户端的确认号、而确认号是客户端的序号+1(反过来了)、此时客户端那边并没有关闭连接、因为有可能服务端可能还有需要发送的数据 注意:这也是为什么需要四次挥手的原因
  • 第三次(服务端发送完数据后会在发送最后的确认)
    • 服务端会在发送一个FIN + ACK来进行最后的确认(序号和确认号不需要改变)
  • 第四次
    • 客户端得到最终的结束确认以后会发送ACK来进行确认、此时客户端的序号来自服务端的确认号+1、确认号来自服务端的序号

image.png

最后为什么一定要四次挥手、目的就是等服务器响应最后数据发送完毕的标识、然后在结束

六 、浏览器解析渲染页面

image.png

拿到服务器资源后、浏览器会执行以下步骤

构建DOM树(DOM tree)
  • 从上到下解析HTML文档生成DOM节点数、也叫内容树

    构建CSSOM树( CSS Object Model)
  • 加载解析样式生成的cssom树

    构建渲染树
  • 根据DOM树和CSSOM树、生成渲染树

    布局( layout)
  • 根据渲染树将每个节点布局到正确的位置

    绘制
  • 遍历渲染树绘制所有节点、为每个节点找到对应的样式

    七、重绘和重排(回流)

    在我们绘制节点的时候、有可能有些因为js动画的原因、可能会导致我们重绘或者重排

重绘

当我们改变一些元素的 背景色、字体颜色、边框颜色 会触发重绘

重排(回流)

  • 添加删除可见的DOM元素
  • 元素的尺寸,位置改变
  • 页面渲染初始化
  • 浏览器尺寸改变
  • 获取元素的计算属性如: offsetTop、offsetLeft、offsetWidth、offsetHeight 、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight、getComputedStyle()、getBoundingClientRect

    重排必定重绘、所以重排性能开销会大于重绘、以下就是一些优化的思路

  • 能重绘尽量不要重排

  • 能少排不多排
  • 能小区域不大区域排 position属性为absolute或fixed
  • 避免无效重排
  • 利用GPU资源

    重排案例和优化

    分离读写操作
    ```javascript // bad div.style.left = ‘10px’; console.log(div.offsetLeft) div.style.top = ‘20px’ console.log(div.offsetTop)

// good div.style.left = ‘10px’; div.style.top = ‘20px’ console.log(div.offsetTop) console.log(div.offsetLeft)

  1. > 浏览器会有重排的优化方案、就是在一定时间内去设置属性他可能会被合并成一次进行重排、但是前提你不能去触发强制重排的方式(**就是获取会影响重排的元素属性**)
  2. <a name="QO88P"></a>
  3. ##### 样式集中修改
  4. ```javascript
  5. // bad
  6. el.style.left = '10px'
  7. el.style.top = '20px'
  8. // good
  9. el.className = 'theclassname'
  10. // good
  11. el.style.cssText = 'left:10px;top:20px;

缓存布局信息
  1. // bad 强制属性、触发两次重排
  2. div.style.left = div.offsetLeft + 1 + 'px'
  3. div.style.top = div.offsetTop + 1 + 'px'
  4. // good 缓存布局信息、相当于读写分离
  5. let curLeft = div.offsetLeft;
  6. let curTop = div.offsetTop;
  7. div.style.left = curLeft;
  8. dic.style.top = curTop;

离线修改DOM

把要修改的元素先通过dipslay:none;去隐藏他、然后修改好了、在设置block显示、原因就是: 不可见元素不会触发重排和重绘!

position属性为absolute或fixed

position属性为absolute或fixed时、重排开销比较小、他不会去考虑其他元素的影响

优化动画

尽量不使用left top、等等去设置元素的移动、可以通过一些CSS3的属性来使用利用GPU去完成、比如 transitions、transforms等等