输入url

108 阅读14分钟

输入url

// 渲染层面

首先先通过 Dns 将URL解析为对应的Ip地址,(减少解析次数)

然后通过Ip地址 建立TCP链接。 (减少握手次数)

发送http请求 (减少请求次数,减少请求体积, 提高访问速度,把静态资源放到c d n)

// 网络层面

处理http请求,http返回客户端数据 (资源加载优化,浏览器h t tp缓存机制, )

拿到响应数据进行解析 (dom树构建,减少重绘重排)

关闭

DNS 解析

在请求的时候,会通过DNS 将域名翻译为对应的IP地址。

全球一共有13组根服务器来用来翻译全部的域名,而域名又分为三类(几个点就是几级域名)

  • 顶级域名 test.com
  • 二级域名 test.cn.com
  • 三级域名 test.mail.cn.com

例如: 当www.baidu.com访问的时候,客户端就会先给dns服务器发送信息, 目前我们的路由都集成了dns。 然后dns 会先给服务器发信息,根服务器返回.com的信息,然后dns 再去给.com发信息,.com返回ip地址给dns,同时dns会记录他的ip地址,最后返回p c,这就是相当于迭代查询

但是,实际上我们的dns都是会缓存的,dns 会先查询浏览器有没有缓存,然后查询电脑有没有,然后去查询运营商有没有,这个就是递归查询,当dns 通过递归的方式去查询本地的dns服务器没有找到的时候,才会去迭代查询像根服务器查询。

引用bilibili的一张图表示

![截屏2021-12-05 下午3.56.37](/Users/lisiyuan/Library/Application Support/typora-user-images/截屏2021-12-05 下午3.56.37.png)

性能优化点之DNS

这里DNS查询也会有一定的时间,所以可以通过两点有关dns来进行优化

  • 减少DNS解析次数
    • 如果网站所有的资源内容都放在同一个域下面,这样的话访问整个网站就只会查找一次DNS, 但是,这样http在下载的时候就会出现资源排队的问题,会增加响应时间,所以,一般找一个折中的域名量
  • 进行DNS预获取, DNS Prefetch
    • DNS Prefetch 是尝试在请求资源之前解析域名,这可能是后面要加载的文件,也可能是用户尝试打开的链接目标。域名解析和内容载入时串行的网络操作,所以这个方式能减少用户的等待时间
    • HTML的link元素通过dns-prefetch的rel属性值提供此功能,然后再href属性中指要跨域的域名。多页面重复DNS预解析会增加DNS查询次数,并且DNS-prefetch仅对跨域的DNS查找有效

建立TCP链接

三次握手 --------> 数据传输 ----------> 四次挥手

先进行三次握手。三次握手实际上是一个 开辟资源,建立连接的过程。

第一次表明客户端可以发送消息,然后第二次表示服务端可以收到消息,也可以发出消息,第三次,表明客户端可以收到消息

  • 客户端建立连接,发送syn包到服务器,等待服务器确认

  • 服务器确认后,回传一个s y n + ack 表示自己收到

  • 客户端收到后,再次发送一个ac k ,表示握手成功,开始进行传送数据

服务器一开始没次处理完一个请求,就关闭连接,然后需要重新进行建立TCP 链接,所以耗费了性能

性能优化点之 减少握手次数

http 1.1默认是持久连接。当客户端第一次请求资源后,并不会断开连接,而是服务器返回消息后,客户端继续发送消息。如果没有要发送的之后,客户端最后发送Connection: close 首部给服务器,这样就会关闭服务器,减少了每一次进行t c p连接

客户端和服务端传送数据

客户端发起http 请求

HTTP优化两个大方向,也就是资源的合并与压缩

  • 减少请求次数
  • 减少单次请求所花费的时间

在将资源打包的时候,通过we b pa c k 进行资源的压缩与合并,利用一些插件优化代码体积,拆分资源。比如tree - sharking, 按需加载等,或者开启http 压缩(在request headers 中加入 accept-encodig: gzip)

图片优化

JPG 使用于较大的背景图,轮播图或者banner图。既可以保证图片质量,也不会有太大的体积,但是他的缺点就是如果图片颜色对比强烈的时候,就会导致图片模糊明显,而且不支持透明度处理。

Png 质量高,但是体积大,体积大, 所以主要是呈现一些小logo,或者颜色简单对比强烈的图片或者背景图

Svg. 体积小,图片放大不失真

一些小图标,都使用css Sprites 来进行一次性请求。

也可以将图片进行base64编码,然后直接写到s r c 中,浏览器会自动解析的,但是如果图片进行base64编码后,图片体积会变为源文件的4/3,所以如果大图也编码的话,会变得非常大,即使少发http请求,但是文件变大,得不偿失。

CDN 用c d n来存放静态资源,c d n 的服务器域名和服务器域名不相同,这样的 话,请求图片或者c s s文件就不会带上cookie

服务端处理http请求

浏览器缓存有

  • Memory Cache
  • Service Worker cache
  • http cache
  • Push cache (HTTP 2 新增的)

http缓存

Http 缓存分为强缓存和协商缓存,当强缓存没有命中的时候,才会走协商缓存。

强缓存

在http1.0 的时候,强缓存利用http 头中的expries 来控制,如果请求发出,服务器返回响应,会在response Headers 中将过期时间写入一个时间过期时间戳,后面再发送请求的时候,浏览器会根据expries 时间戳和本地时间进行判断, 判断目标有没有命中强缓存,如果命中,则直接从缓存中获取资源,返回http状态码为200,不会再与服务端通信,但是,本地时间可能会被修改,也可能服务器时间与本地时间设置不一致,所以, Http 1.1 中新增了 Cache-Control 字段 来代替expires, cache-control: max-age = 124435450, 通过max-age 来设置一个时间长度,在这个时间段内,缓存都有效,cache-Control 和expries 同时出现,要以ca che-control 为准。 除了max-age 外。还有s-maxage, 如果s-maxage 和max-age 同时出现,则先考虑s-maxage, 如果s-maxage没有过期,就去向代理服务器请求缓存内容,但是是只针对将资源设置为pubic 的缓存。

Cache-control: max-age=1200, s-maxage=340000

如果资源设置为public, 则资源既可以被浏览器缓存,也可以被代理服务器缓存,如果设置为private, 则资源只能被浏览器缓存。

如果设置 Cache-control: no-cache 后,则每一次请求都不会询问浏览器缓存,不会走强缓存,而是直接向服务端确认资源是否过期,直接去走协商缓存

如果设置 Cache-control: no-store 。则是不会使用任何缓存策略,直接向服务器发送请求,下载响应资源。

**协商缓存 **(判断缓存还有没有效)

浏览器去向服务器确认缓存相关信息,来判断缓存是不是还有效,用来判断重新发起请求,还是读取本地缓存的资源。如果服务器提示资源没有改动,则资源会被重定向到浏览器缓存中,网络返回304状态码。

如果开启协商缓存,在第一次发起请求的时候,response header 会返回一个 last-Modified:fri, 1 mon 2021 12:44:52 GMT, 然后 后面每次请求,请求头都会带上一个叫 if-Modified-Since: fri, 1 mon 2021 12:44:52 GMT 字段,值就是last-Modified 返回的值, 然后服务端接受到后,对比该时间和服务端资源返回的时间是不是一致,如果没有修改,则返回304响应, 如果修改了,则 response-header 中添加新的last-modified时间。

但是last-modified 中,如果资源保存后,但是并没有修改任何东西,这时候,last-modified也会再次改变,资源再次请求的时候,会被当作新资源,还有就是他只能检查到以秒为单位,没办法检查到毫秒时间的修改,所以,如果在毫秒时间修改了文件,last-modified并不会更新。

为了解决这两个问题,出现了Etag, Etag 是服务器为每个资源生成了一个资源唯一标识字符串,文件内容不同,etag的值就不同,第一次请求的时候,response-headers 返回etag的值,再次请求的时候,请求头都会带上一个 if-None-Match, 值为etag的返回值,然后服务端去进行对比,如果Etag 和 last-modified 同时存在,以etag为准。但是etag 生成需要服务器的额外开销。这是他的缺点。

本地存储

http 是一个无状态协议,当服务器接受客户端请求返回响应的时候,并不会记录当前客户端是谁,结束后,下一次接收到客户端的请求并不知道当前客户端与刚刚是否相同,所以诞生了cookie.

cookie 大小只有4kb, 通过响应头里面的set-cookie 来指定要存的cookie,domain 为设置cookie页面的主机名, 同一个域名下所有请求都会携带cookie

set-cookie: name=lalala;domain=juejin.cn

Local Storage

存储大小为5M(5M代表字符串长度或者说10M 字节数),一般用他存储一些base64字符串,或者j s,c s s,等不长更新的资源

session Storage

只适用于当前会话,关闭后就会自动清除,刷新不会

INdexDB

新增的非关系型数据库 一般来说不会小于200M

渲染页面构建dom

客户端渲染,服务器把静态文件发送给客户端,客户端加载后,在浏览器运行一遍js,然后根据结果返回生成对应dom, 页面上的内容在html源文件里找不到,客户端渲染除了要加载html,还要等渲染所需的j s加载完后,在运行一遍j s,才能真正展示出来,所以容易出现白屏,解决的办法就是使用服务端渲染,直接展示一个完整的网页,而且服务端渲染还可以进行搜素引擎快速展示,seo优化。

通过浏览器内核中几大部分,来进行渲染网页。主要有

  1. HTML解析器 将html文档经过词法分析输出,然后生成dom树
  2. Css 解析器: 解析c s s文档,生成样式规则,创建c s sdom树
  3. 图层布局计算模块: 布局计算每个对象的精确位置和大小
  4. 视图绘制模块: 进行具体节点的图像绘制,将像素渲染到屏幕上
  5. javascript 引擎。编译执行javascript 代码

每一个页面都经历了这几个阶段

  • 解析html, 在解析过程中发出了页面渲染所需要的各种外部资源
  • 计算样式,识别加载所有的c s s样式与dom树结合,生成ren de r树(cssdo m解析过程与do m 解析过程是并行的)
  • 计算图层布局。计算元素的位置大小
  • 绘制图层 根据dom 代码结果,给每一个图层转为像素
  • 合并图层 合并图层,通过GPU绘制到屏幕上

新元素加入do m树的时候,c s s 就会查c s s 样式表,找到符合的样式应用到元素上,然后重新绘制,如何优化c s s样式表

c s s 规则都是从右边到左边匹配,所以少用标签选择器,减少嵌套层级

Css js 加载顺序

c s Odom 和do m树合并成render tree, 默认情况下c s s是阻塞资源的,do m 解析完成,c s sdo m未完成,渲染就需要等着c s sdo m完成,所以,尽早将c s s 下载,比如将c s s 放到header 里,和启用c dn实现静态资源加载速度优化。

**j s引擎是独立于渲染引擎的,j s 在文档哪里插入,就在哪里执行,**当html解析器遇到script 时候,就会暂停渲染过程,将控制权交给j s引擎。j s 对内联j s代码直接执行,对外部ji s需要先获取脚本然后执行,等j s执行完毕,再将控制权交给渲染引擎,来进行c s sdom 和do m 的构建。因为浏览器不知道j s 会对页面做什么改变,所以加载j s 会阻塞其他。

通过控制j s 加载时机优化

ji 加载三种方式。正常加载。会阻塞c s sdom 和do m 构建

async 模型 不会阻塞浏览器做其他事情,j s加载是异步的,加载完后立即执行。

de f er 模式 不会阻塞浏览器,也是异步加载j s, 但是j s执行被推迟。只有等文档解析完成后,才会开始执行j s文件。

减少操作dom。 减少重绘与回流,利用dom Fragment

Js 修改do m 本质上是修改了renderTree 变换导致。

重绘和重排主要是修改do m 的时候触发。

当对do m 修改宽。高,或者隐藏元素的时候,浏览器就需要重新计算元素的属性,然后再将绘制图层。这个过程就是回流。也叫重排,重排一定会引起重绘

当对do m 修改其样式如背景颜色,没有修改几何样式,不需要计算位置,直接绘制新的样式,就是重绘。

如何避免。如果频繁改动一个元素,需要进行多次计算。 这样会触发多次回流,那么我们可以将do m 设置为display:none., 然后计算完成后再进行display: block,这样只触发一次回流,尽量使用class.,

事件循环

因为js 是单线程的(因为涉及到do m操作,多线程需要进行同步判断),但是遇到定时逻辑,网络请求等异步操作又回阻塞线程,所以,当代码执行的是,遇到同步任务直接执行,遇到异步任务就会根据任务类型分别放到宏任务队列和微任务队列,同步任务执行完后,查看微任务队列,如果有微任务,则保证把所有的微任务全部执行完(包括执行微任务中产生的新的微任务), 然后去执行查看宏任务队列,执行下一个宏任务,如果在执行宏队列的时候,产生了新的微任务,则执行完微任务再去接着执行宏队列 ,执行完后,查看微任务队列,重复循环,直到宏任务队列为空。

宏任务: setTimeout. setInterval, ajax , fetch,

微任务: promise.then() mutationObserver. Object.observe

关闭TCP连接

四次挥手

  • 客户端向服务端发起一个fi n 报文
  • 服务端收到fi n 后,会发给客户端一个fi n + ac k 的报文
  • 如果服务器也想断开链接,给客户端发一个fi n
  • 客户端收到fi n 后,给服务器发一个ac k, 进入time_wait然后过一段时间确保服务器收到a c k 后,才关闭

服务端收到a ck 后直接关闭,如果给服务端的a c k 丢失了,服务端会重新发fi n + ack, 当客户端再次收到a c k 后,就知道之前ac k丢失了,再重新发送ac k