阅读 67

HTTP网络层基础知识(URL输入地址栏经历了什么)

彻底掌握基于HTTP网络层的 “前端性能优化”

产品性能优化方案

  • HTTP网络层优化
  • 代码编译层优化 webpack
  • 代码运行层优化 html/css + javascript + vue + react
  • 安全优化 xss + csrf
  • 数据埋点及性能监控
  • ……

CRP(Critical [ˈkrɪtɪkl] Rendering [ˈrendərɪŋ] Path)关键渲染路径

问题:从输入URL地址到看到页面,中间都经历了啥

第一步url解析
  • 传输协议:HTTP / HTTPS (SSL) / FTP (文件上传) ...
  • 域名:顶级域名、一级域名、二级域名
    • 购买的是顶级域名
    • 主域相同子域不同属于跨域
  • 端口号:0~65535 【HTTP:80 HTTPS:443 FTP: 21 默认端口号是对浏览器处理的】
    • 问号参数:可以把一些信息传递给服务器“GET”系列;也可以实现两个页面之间的信息通信;SPA单页面中,实现组件和组件之间的通信
  • HASH哈希值:锚点定位:HASH路由:...

编码问题URL中特殊内容的编码

​ encodeURI decodeURI 编译能力弱(一般编译一整段http)

​ encodeURIComponent decodeURIComponent 编译能力强(一般编译一部分http中的汉字)

​ escape unescape 客户端之间的编译 比如我要把汉字编译了存cookie

image-20210528182948499.png

第二步:缓存检查
  • 先查找内存然后查找硬盘

  • F5普通刷新:内存 => 硬盘

  • 重新打开页面:硬盘

  • CTRL+F5强制刷新:不检查任何缓存直接向服务的发送请求

    @1 强缓存 (服务器设置)
    • 先检测本地是否有前缓存,有、且没有过期、直接本地获取、然后渲染【HTTP状态码200】
    • 如果没有或者过期了、则重新向服务器发送请求、拉取最新的结果、渲染的同时、把本次结果缓存起来
    • ....

    优势:@1 性能优化的重要手段,可以保证第二次及其以后再次访问产品速度会很快...

    弊端:@1 HTTP是不能做缓存的

    ​ @2 只要HTTP不做强缓存我们就可以保证其资源及时更新,请求资源后面设置时间戳 或者 文件名字根据内容生成HASH名...

    服务器设置设置的强缓存:在每一次重新从服务器拉取最新资源文件的时候,都在请求头中携带Expires /Cache-Control'缓存有效期',客户端自动帮我们把信息缓存起来,包含他的有效期....以后再访问这些资源就看本地是否有,以及是否过期了。

    • Expires:缓存过期时间,用来指定资源到期的时间(HTTP/1.0)
    • Cache-Control:cache-control: max-age=2592000第一次拿到资源后的2592000秒内(30天),再次发送请求,读取缓存中的信息(HTTP/1.1)
    • 两者同时存在的话,Cache-Control优先级高于Expires

image-20210528185958406.png

协商缓存 Last-Modified / ETag 【304只有强缓存失效之后才会校验协商缓存】
  • Last-Modified / ETag存储起来Last-Modified表示当前资源文件在服务器最后修改时间ETag每次修改都会生成一个标记
  • 再次请求这个页面,强缓存不生效,开始协商缓存
    • 向服务器发送请求,同时带上if-Modifyed-Since:Last-Modified if-None-Match:ETag传给服务器;服务器会根据传递的时间/标志,和服务器本色的资源文件最后修改的时间/标志,进行对比,如果一样说明文件没有更新过,直接返回304即可,无需返回内容,如果不一样,说明更新过,那么返回最新内容和最新的Last-Modified/ETag...

HTML文件资源可以使用协商缓存

image-20210528192316583.png

数据缓存

image-20210528192443462.png

第三步:DNS解析
  • 在DNS服务器上,基于域名找到外网IP,后面我们就可以基于服务器外网IP找到服务器

image-20210528192837030.png

  • DNS预获取 (获取到本地)

    <meta http-equiv="x-dns-prefetch-control" content="on">
    <link rel="dns-prefetch" href="//static.360buyimg.com"/>
    <link rel="dns-prefetch" href="//misc.360buyimg.com"/>
    <link rel="dns-prefetch" href="//img10.360buyimg.com"/>
    <link rel="dns-prefetch" href="//d.3.cn"/>
    <link rel="dns-prefetch" href="//d.jd.com"/>
    复制代码
  • 大项目一般都会服务器拆分

    服务器拆分的优势

    • 资源合理利用

    • 抗压能力增强

    • 提高HHTP并发

    • ...

image-20210528193809293.png

  • 可以修改本地的HOST解决跨域跨域

    • HOST

image-20210528194114508.png

第四步:TCP三次握手

image-20210528194245590.png

image-20210528194522549.png

第五步:数据传输
第六步:TCP四次挥手

image-20210529091914144.png

Connection: keep-alive长连接

第七步:页面渲染
总结步骤:
+ 处理 HTML 标记,构建 DOM 树 
+ 处理 CSS 标记,构建 CSSOM 树
+ 将 DOM 树和 CSSOM 树融合成渲染树
+ 根据生成的渲染树,计算它们在设备视口(viewport)内的确切位置和大小,这个计算的阶段就是回流 => 布局(Layout)或 重排(reflow)
+ 根据渲染树以及回流得到的几何信息,得到节点的绝对像素 => 绘制(painting)
复制代码

image-20210529132146238.png

浏览器的主要进程和职责

image-20210529132405311.png

什么是 CRP,即关键渲染路径(Critical Rendering Path)? 如何优化

关键渲染路径是浏览器将 HTML CSS JavaScript 转换为在屏幕上呈现的像素内容所经历的一系列步骤。也就是我们上面说的浏览器渲染流程。

我们要从以下三个纬度进行优化

  • 关键资源数量:请求资源数量
  • 关键路径长度:获取所有关键资源所需的往返次数或总时间
  • 关键字节: 实现网页首次渲染所需的总字节数,等同于所有关键资源传送文件大小的总和。

优化DOM

  • 删除不必要的代码和注释包括空格,尽量做到最小化文件。
  • 可以利用 GZIP 压缩文件
  • HTTP缓存文件

CSSOM

  • 减少关键 CSS 元素数量
  • 减少CSS嵌套
  • style/link/减少@import

js

  • async

  • defer

  • 预加载

  • DNS预处理

image-20210529133655174.png

谈谈浏览器的回流与重绘
  • 回流必将引起重绘,重绘不一定会引起回流。
  • 操作DOM可能会引起浏览器的回流(比如DOM位置发生变化)这就是操作DOM效率低的原因

哪些操作会引起回流

  • 页面的首次渲染
  • 浏览器窗口大小发生改变
  • 元素尺寸或位置发生改变元素内容变化(文字数量或图片大小等等)
  • 元素字体大小发生改变
  • 添加或者删除可见的 DOM 元素

重绘(Repaint)

  • 当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility 等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。

如何减少回流和重绘

  • 放弃传统操作dom的方式,基于vue/react开始数据影响视图的模式

  • 读写分离

    浏览器的队列渲染机制

    • 获取样式刷新浏览器渲染队列
  • 文档碎片

    let box = document.querySelector('#box'),
        frag = document.createDocumentFragment();
    for (let i = 0; i < 10; i++) {
        let span = document.createElement('span');
        span.innerHTML = i + 1;
        frag.appendChild(span);
    }
    box.appendChild(frag);
    
    /*----*/
    let box = document.querySelector('#box'),
        str = ``;
    for (let i = 0; i < 10; i++) {
        str += `<span>${i+1}</span>`;
    }
    box.innerHTML = str;
    复制代码
  • css3硬件加速(GPU加速)

    • transform \ opacity \ filters

性能优化汇总

/*
 *     1.利用缓存
 *       + 对于静态资源文件实现强缓存和协商缓存(扩展:文件有更新,如何保证及时刷新?)  
 *       + 对于不经常更新的接口数据采用本地存储做数据缓存(扩展:cookie / localStorage / vuex|redux 区别?)
 *     2.DNS优化
 *       + 分服务器部署,增加HTTP并发性(导致DNS解析变慢)
 *       + DNS Prefetch
 *     3.TCP的三次握手和四次挥手
 *       + Connection:keep-alive
 *     4.数据传输
 *       + 减少数据传输的大小
 *         + 内容或者数据压缩(webpack等)
 *         + 服务器端一定要开启GZIP压缩(一般能压缩60%左右)
 *         + 大批量数据分批次请求(例如:下拉刷新或者分页,保证首次加载请求数据少)
 *       + 减少HTTP请求的次数
 *         + 资源文件合并处理
 *         + 字体图标
 *         + 雪碧图 CSS-Sprit
 *         + 图片的BASE64
 *       + ......
 *     5.CDN服务器“地域分布式”
 *     6.采用HTTP2.0
 * ==============
 * 网络优化是前端性能优化的中的重点内容,因为大部分的消耗都发生在网络层,尤其是第一次页面加载,如何减少等待时间很重要“减少白屏的效果和时间”
 *     + LOADDING 人性化体验
 *     + 骨架屏:客户端骨屏 + 服务器骨架屏
 *     + 图片延迟加载
 *     + ....
 */
复制代码

HTTP1.0 VS HTTP1.1 VS HTTP2.0

image-20210529092635305.png

HTTP1.0和HTTP1.1的一些区别
  • 缓存处理HTTP1.0使用last-modified,expires来作为缓存的判断标准,HTTP1.1则引入了更多缓存控制策略:ETag,Cache-control...
  • 带宽优化及网络连接使用HTTP1.1支持断点续传,即返回码是206(Partial Content)
  • 错误通知的管理,在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除…
  • Host头处理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)
  • 长连接,HTTP1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点
HTTP2.0和HTTP1.X相比的新特性
  • 新的二进制格式(Binary Format),HTTP1.x的解析是基于文本,基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合,基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮
  • header压缩,HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小
  • 服务端推送(server push),例如我的网页有一个sytle.css的请求,在客户端收到sytle.css数据的同时,服务端会将sytle.js的文件推送给客户端,当客户端再次尝试获取sytle.js时就可以直接从缓存中获取到,不用再发请求了
  • 多路复用(MultiPlexing)
- HTTP/1.0  每次请求响应,建立一个TCP连接,用完关闭
- HTTP/1.1 「长连接」 若干个请求排队串行化单线程处理,后面的请求等待前面请求的返回才能获得执行机会,一旦有某请求超时等,后续请求只能被阻塞,毫无办法,也就是人们常说的线头阻塞;
- HTTP/2.0 「多路复用」多个请求可同时在一个连接上并行执行,某个请求任务耗时严重,不会影响到其它连接的正常执行;
复制代码

细说一下GET / HEAD / DELETE / OPTIONS

GET

GET可以说是最常见的了,它本质就是发送一个请求来取得服务器上的某一资源。资源通过一组HTTP头和呈现据(如HTML文本,或者图片或者视频等)返回给客户端。GET请求中,永远不会包含呈现数据。

HEAD

HEAD和GET本质是一样的,区别在于HEAD不含有呈现数据,而仅仅是HTTP头信息。有的人可能觉得这个方法没什么用,其实不是这样的。想象一个业务情景:欲判断某个资源是否存在,我们通常使用GET,但这里用HEAD则意义更加明确。

app.head('/head',((req,res) => {
    res.type('application/json').send('ok')
}))
复制代码

image-20210607204039578.png

使用场景
  • 检查一个文件是否存在可以用 HEAD 请求,没有必要用 GET 把整个文件都取下来。
  • 检查文件是否有最新版本,也可以使用 HEAD,服务器会在响应头里把文件的修改时间传回来。
注意:

image-20210607205141207.png

express中的get请求也可以就收head请求

image-20210607205243094.png

OPTIONS

这是浏览器对复杂跨域请求的一种处理方式,在真正发请求之前会发送一个OPTIONS请求用来试探服务器响应是否正确如果状态错误他会停止第二次真正的请求

什么是复杂请求

image-20210607214949182.png

// 设置跨域
app.use((req, res, next) => {
    res.header('Access-Control-Allow-Origin', "http://127.0.0.1:5500");
    //响应首部 Access-Control-Allow-Headers 用于 preflight request (预检请求)中
    res.header('Access-Control-Allow-Headers', "Origin, X-Requested-With, Content-Type, Accept, Authorization");
    if(req.method ==='OPTIONS'){
        res.send('ok')
    } 
    next();
})
复制代码

DELETE

请求服务器删除Request-URL所标识的资源(语义化

这玩意好像就是告诉你我要删除文件了没啥特别的地方

app.delete('/delete', function (req, res) {
    res.send('DELETE request to homepage')
})
复制代码

PUT

PUTPOST 方法的区别在于,PUT方法是幂等的:调用一次与连续调用多次是等价的(即没有副作用),而连续调用多次POST方法可能会有副作用,比如将一个订单重复提交多次。

@1如果是更新,并不会产生新的数据,新的数据会覆盖老的数据,用put,如果是创建,会产生新的数据,则用post。

@2修改类的操作,比如修改用户名、修改密码,是幂等的,所以为put请求。增加操作不是幂等的。

patch

局部更新

总结:

即使我只用POST,甚至自己搞一个Create的HTTP Method,一样可以完成所有功能。标准存在的意义就在于给大家提供一套默认广泛通用的细则,如果大家都遵循,那么沟通起来,系统对接起来,是会省去很多不必要的成本的,这些标准的意义很大一部分就在于最大力度的来降低这种成本。

文章分类
前端
文章标签