https://segmentfault.com/a/1190000019897234
从输入网址看方向加载
从网址输入到页面加载:
首先做 DNS 查询,如果这一步做了智能 DNS 解析的话,会提供访问速度最快的 IP 地址回来接下来是 TCP 握手,应用层会下发数据给传输层,这里 TCP 协议会指明两端的端口号,然后下发给网络层。网络层中的 IP 协议会确定 IP 地址,并且指示了数据传输中如何跳转路由器。然后包会再被封装到数据链路层的数据帧结构中,最后就是物理层面的传输了TCP 握手结束后会进行 TLS 握手,然后就开始正式的传输数据数据在进入服务端之前,可能还会先经过负责负载均衡的服务器,它的作用就是将请求合理的分发到多台服务器上,这时假设服务端会响应一个 HTML 文件首先浏览器会判断状态码是什么,如果是 200 那就继续解析,如果 400 或 500 的话就会报错,如果 300 的话会进行重定向,这里会有个重定向计数器,避免过多次的重定向,超过次数也会报错浏览器开始解析文件,如果是 g 格式的话会先解压一下,然后通过文件的编码格式知道该如何去解码文件文件解码成功后会正式开始渲染流程,先会根据 HTML 构建 DOM 树,有 CSS 的话会去构建 CSSOM 树。如果遇到 标签的话,会判断是否存在 async 或者 defer ,前者会并行进行下载并执行 JS,后者会先下载文件,然后等待 HTML 解析完成后顺序执行,如果以上都没有,就会阻塞住渲染流程直到 JS 执行完毕。遇到文件下载的会去下载文件,这里如果使用 HTTP 2.0 协议的话会极大的提高多图的下载效率。初始的 HTML 被完全加载和解析后会触发 DOMContentLoaded 事件CSSOM 树和 DOM 树构建完成后会开始生成 Render 树,这一步就是确定页面元素的布局、样式等等诸多方面的东西在生成 Render 树的过程中,浏览器就开始调用 GPU 绘制,合成图层,将内容显示在屏幕上了从输入网址到显示页面,主要有三个过程,涉及网络层面:
DNS 解析TCP 连接HTTP 请求/响应对于DNS解析和TCP连接,我们前端能做的努力非常有限。相比之下,HTTP连接的优化是我们网络优化的核心。
HTTP优化有两个主要方向:
减少请求次数减少单次请求所花费的时间浏览器缓存策略
浏览器缓存机制有四个方面,根据获取资源时请求的优先级排列如下:
Memory CacheService Worker CacheHTTP CachePush Cache内存缓存
MemoryCache指存储在内存中的缓存。从优先级来说,它是浏览器尝试命中的第一个缓存。从效率上来说,是响应速度最快的缓存。浏览器坚持“节约原则”。我们发现Base64格式的图片几乎总能被塞进内存缓存,这可以看作是浏览器节省渲染费用的“自我保护行为”;此外,小容量的JS和CSS文件写入内存的几率更大——相比之下,较大的JS和CSS文件没有这种处理,内存资源有限,所以往往直接扔到磁盘上。
服务工作者缓存
服务工作者是一个独立于主线程的Java线程。它与浏览器表单分离,因此无法直接访问DOM。这种独立的人格使得Service Worker的“个人行为”无法干扰页面的性能,而这个“幕后工作者”可以帮助我们实现离线缓存、消息推送、网络代理等功能。我们在服务人员的帮助下实现的离线缓存称为服务人员缓存。
超文本传输协议缓存
分为强缓存和协商缓存。优先级较高的是强缓存,只有当对强缓存的命中失败时,才会使用协商缓存。
http get消息的基本缓存过程包括七个步骤:
接收解析查询,缓存查看是否有本地副本可用,如果没有,就获取一份副本新鲜度检测, 缓存查看已缓存副本是否足够新鲜,如果不是,就询问服务器是否有任何更新。创建响应,缓存会用新的首部和已缓存的主体来构建一条响应报文。发送,缓存通过网络将响应发回给客服端。日志强缓存
强缓存由两个字段控制:http头中的过期和缓存控制。在强缓存中,当请求再次发送时,浏览器会根据过期和缓存控制来判断目标资源是否“命中”强缓存。如果命中,它将直接从缓存中获取资源,不会与服务器通信。
够新鲜吗?
通过expires:xxxxxxxx GMT(绝对日期和时间,http/1.0)或Cache-Control:max-age=XXXX(相对日期和时间,http/1.1)标记文档中的到期日期。
与过期相比,缓存控制更准确,优先级更高。当缓存控制和过期同时出现时,我们将以缓存控制为标准。
关键词理解
公有和私有是一组相对立的概念,针对的是资源是否可以被代理服务缓存。如果我们为资源设置公共,它可以被浏览器和代理服务器缓存。如果我们设置为私有,资源只能被浏览器缓存。Private是默认值。
无存储无缓存,无缓存绕过浏览器:我们为资源设置无缓存后,每次发起请求时,都不会询问浏览器的缓存情况,而是直接从服务器确认资源是否过期(也就是说,我们会按照后面解释的协商缓存的路线)。无存储相当粗鲁,顾名思义,它不使用任何缓存策略。在无缓存的基础上,它绕过了服务器的缓存确认,只允许你直接向服务器发送请求,下载完整的响应。
协商缓存
协商缓存取决于服务器和浏览器之间的通信。在协商缓存机制下,浏览器需要向服务器询问缓存的信息,然后决定是重新发起请求、下载完整的响应,还是在本地获取缓存的资源。如果服务器提示缓存资源未被修改,资源将被重定向到浏览器缓存。在这种情况下,对应于网络请求的状态码是304。
协商缓存的实现:从Last-Modified到Etag,详细在百度,这里不详细展开。
HTTP缓存决策
当我们的资源内容不可重用时,直接为Cache-Control设置无存储,拒绝一切形式的缓存;否则,考虑是否需要每次都向服务器确认缓存有效性,如果需要,则将Cache-Control的值设置为无缓存;;否则,考虑资源是否可以被代理服务器缓存,决定设置为私有还是公共;根据所述结果对所述图像进行处理;然后考虑资源的到期时间,设置相应的max-age和s-maxage值;最后,配置协商缓存所需的参数,如Etag和最后修改。
Push Cachae
推送缓存是指服务器推送阶段的HTTP2的缓存。
Push Cache 是缓存的最后一道防线。浏览器只有在 Memory Cache、HTTP Cache 和 Service Worker Cache 均未命中的情况下才会去询问 Push Cache。Push Cache 是一种存在于会话阶段的缓存,当 session 终止时,缓存也随之释放。不同的页面只要共享了同一个 HTTP2 连接,那么它们就可以共享同一个 Push Cache。CDN对此有所了解
CDN有两个核心点,一个是缓存,一个是返回源。
“缓存”指的是将资源复制到CDN服务器的过程,“返回源”指的是CDN发现自己没有这个资源(通常缓存的数据已经过期)并转向根服务器(或其上层服务器)索要这个资源的过程。
CDN常用于存储静态资源。所谓“静态资源”,就是JS、CSS、图片等资源。,无需服务服务器即可计算。而“动态资源”,顾名思义,就是需要后端实时动态生成的资源,比较常见的是依靠服务器渲染的JSP、ASP或者HTML页面。
「非纯静态资源」呢?它指的是要求服务器在页面外进行额外计算的HTML页面。具体来说,在我打开网站之前,网站需要通过权限认证等一系列手段确认我的身份,然后决定是否向我呈现HTML页面。在这种情况下,HTML确实是静态的,但是它是和业务服务器的操作耦合在一起的,所以我们把它扔在CDN上显然是不合适的。
另外,CDN的域名必须与主营业务服务器的域名不同。否则同一个域名下的cookies到处跑,浪费性能流量的开销。CDN域名放在不同的域名下,可以完美避免不必要的cookies!
图像优化
二进制数字和颜色之间的关系
在计算机中,像素由二进制数表示。在不同的图片格式中,像素和二进制数字的对应关系是不同的。像素对应的二进制数字越多,它可以代表的颜色就越多,成像效果就越好,文件量就越大。
一个二进制位代表两种颜色(0|1对应黑色|白色)。如果有N个二进制位对应一个图片格式,它可以呈现2 N种颜色。
计算图片大小
对于100,100像素的图片,图片上有10,000个像素。如果每个像素的值是由RGBA存储的,也就是说,每个像素有4个通道,每个通道有1个字节(8位= 1字节),那么图片大小约为39KB(10000 1 * 4/1024)。
但是在实际项目中,一张图片可能不需要显示这么多颜色,所以我们可以通过减少每个像素的调色板来减小图片的大小。
知道了如何计算图片的大小,对于如何优化图片一定要有两个思路:
减少像素点减少每个像素点能够显示的颜色图片类型要点
JPEG/JPG的特点:有损压缩,体积小,加载快,不支持透明。JPG最大的特点是有损压缩。这种高效的压缩算法使它成为一种非常轻便的图像格式。另一方面,即使被称为“有损”压缩,JPG的压缩方法仍然是一种高质量的压缩方法:当我们将图像体积压缩到原始体积的50%以下时,JPG仍然可以保持60%的质量。但在处理线条强烈、色彩对比强烈的图像时,如矢量图形、Logo等,人为压缩造成的图像模糊会相当明显。
PNG特点:无损压缩,高质量,大尺寸,支持透明。PNG(便携式网络图形格式)是一种具有无损压缩的高保真图片格式。8和24,其中是二进制数的位数。根据我们前知中提到的对应关系,8位PNG最多可以支持256种颜色,而24位PNG可以呈现1600万种左右的颜色。PNG图片比JPG的色彩表现力更强,线条处理更细腻,对透明度的支持更好。它弥补了上面提到的JPG的局限性,唯一的BUG就是太大了。
SVG的特点:文本文件,体积小,不失真,兼容性好。可伸缩矢量图形是一种基于XML语法的图像格式。它与本文提到的其他种类的图片有本质的不同:SVG的图像处理不是基于像素,而是基于图像的形状描述。
Base64特性:文本文件、相关编码和小图标解决方案。Base64不是图片格式,而是编码方式。Base64和Sprite一样,是作为小图标解决方案存在的。
WebP的特点:年轻全能玩家,喜欢JPEG,WebP可以轻松处理细节丰富的图片,支持PNG那样的透明,显示GIF那样的动态图片——它结合了各种图片文件格式的优点。但毕竟年轻,兼容性有些问题。
渲染优化
客户端渲染
在客户端呈现模式下,服务器将向客户端发送呈现所需的静态文件。客户端加载后,会在浏览器中运行JS,并根据JS的运行结果生成相应的DOM。页面上呈现的内容,你在html源文件中找不到——这是它的特点。
服务器渲染
在服务器端呈现模式下,当用户第一次请求页面时,服务器将所需的组件或页面呈现为一个HTML字符串,然后将其返回给客户端。网页上呈现的内容也可以在html源文件中找到。服务器端渲染解决了一个关键的性能问题——首屏加载慢,也解决了SEO搜索引擎的问题。
浏览器渲染过程分析
浏览器的渲染机制一般分为以下几个步骤:
处理 HTML 并构建 DOM 树。处理 CSS 构建 CSSOM 树将 DOM 与 CSSOM 合并成一个渲染树。根据渲染树来布局,计算每个节点的位置。调用 GPU 绘制,合成图层,显示在屏幕上。渲染DOM时,浏览器实际上做的是:
获取DOM后分割为多个图层对每个图层的节点计算样式结果(Recalculate style–样式重计算)为每个节点生成图形和位置(Layout–回流和重布局)将每个节点绘制填充到图层位图中(Paint Setup和Paint–重绘)图层作为纹理上传至GPU复合多个图层到页面上生成最终屏幕图像(Composite Layers–图层重组)基于渲染过程的CSS优化建议
CSS选择器从右向左匹配。例如,#myList li {}实际上成本很高。
避免使用通配符,只对需要用到的元素进行选择。关注可以通过继承实现的属性,避免重复匹配重复定义。少用标签选择器。如果可以,用类选择器替代。错误:#dataList li{} 正确:.dataList{}不要画蛇添足,id 和 class 选择器不应该被多余的标签选择器拖后腿。错误:.dataList#title 正确:#title减少嵌套。后代选择器的开销是最高的,因此我们应该尽量将选择器的深度降到最低(最高不要超过三层),尽可能使用类来关联每一个标签元素。CSS的阻塞
CSS是被屏蔽的资源。在构建CSSOM的过程中,浏览器不会呈现任何经过处理的内容。即使DOM已经解析了,只要CSSOM不行,渲染也不行。我们把CSS放在头标签里,尽快启用CDN,优化静态资源的加载速度。
JS的阻塞
JS引擎独立于渲染引擎。我们的JS代码在插入文档的任何地方都会被执行。当HTML解析器遇到一个标记时,它会暂停渲染过程,并将控制权交给JS引擎。JS引擎会直接执行内联JS代码,得到脚本后会执行外部JS文件。JS引擎运行后,浏览器会将控件返回到渲染引擎,继续构建CSSOM和DOM。
DOM渲染的优化
首先了解回流和重绘
回流:当我们对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。这个过程就是回流(也叫重排)。重绘:当我们对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(跳过了上图所示的回流环节)。这个过程叫做重绘。重绘不一定会导致回流,但回流一定会导致重绘。回流做的事情比画画多,带来的费用也多。在开发中,有必要从代码层面最大限度地减少回流和重绘的次数。
实例分析
& lt!DOCTYPE html>。
& lthtmllang="en " >
& lthead>。
& ltmetacharset="UTF-8 " >;
& ltmeta name = " viewport " content = " width = device-width,initial-scale=1.0 " >
& ltmeta http-equiv = " X-UA-Compatible " content = " ie = edge " >;
& lttitle>。DOM操作测试
& lt/head>。
& ltbody>。
& ltdivid="container ">。& lt/div>。
& lt/body>。
& lt/html>。
for(var count = 0;计数<。10000;count++){
document.getElementById('容器')。innerHTMl+= ' & lt;span>。我是一个测验
}
进化一:
//只获取容器一次
let container = document . getelementbyid(' container ')
for(let count = 0;计数<。10000;count++){
container . innerHTMl+= ' & lt;span>。我是一个测验
}
进化2:
//减少不必要的DOM更改
let container = document . getelementbyid(' container ')
letcontent = ' '
for(let count = 0;计数<。10000;count++){
//先操作内容
content+= ' & lt;span>。我是一个测验
}
//内容被处理,最后触发DOM改变。
container.innerHTML = content
其实考虑到JS的运行速度,比DOM快多了。减少DOM操作的核心思想是让JS划分DOM。
在文档片段中,文档片段接口表示没有父文件的最小文档对象。它被用作轻量级文档,用于存储已经格式化或尚未格式化的XML片段。因为DocumentFragment不是真正的DOM树的一部分,它的改变不会引起DOM树的重渲染操作,也不会引起性能问题。
进化三:
let container = document . getelementbyid(' container ')
//创建一个DOM片段对象作为容器
let content = document . createdocumentfragment
for(let count = 0;计数<。10000;count++){
//此时可以通过DOM API创建span
letoSpan =文档。(“跨度”)
OSpan.innerHTML = '我是小测试'
//像真实的DOM一样操作DOM片段对象
内容。(oSpan)
}
//对内容进行处理,然后触发真正的DOM变化。
集装箱。(内容)
进化四:
当渲染涉及数千个色调的数据,要求不堵塞画面时,如何解决?
如何在不干扰页面的情况下渲染数据,也就是说数万个页面不能一次渲染,而部分DOM要一次渲染,所以可以通过requestAnimationFrame每16 ms刷新一次。
& lt!DOCTYPE html>。
& lthtmllang="en " >
& lthead>。
& ltmetacharset="UTF-8"/>
& ltmeta name = " viewport " content = " width = device-width,initial-scale=1.0"/>
& ltmeta http-equiv = " X-UA-Compatible " content = " ie = edge "/>
& lttitle>。文件<。/title>。
& lt/head>。
& ltbody>。
& ltul>。
控制
& lt/ul>。
& lt>。
setTimeout(= >;{
//插入十万条数据
consttotal = 100000
//一次插20块,觉得性能不好就减。
constonce = 20
//渲染数据需要多少次
常量循环计数=总计/一次
letcountOfRender = 0
let ul = document . query selector(' ul ')
functionadd() {
//优化性能,插入不会造成回流
const fragment = document . createdocumentfragment
for(leti = 0;i <。一次;i++) {
constli = document。(“里”)
Li . inner text = math . floor(math . random * total)
碎片。(li)
}
ul。(片段)
countOfRender += 1
环
}
functionloop() {
if(countofronder & lt;loopCount) {
window.requestAnimationFrame(添加)
}
}
环
}, 0)
& lt/>。
& lt/body>。
& lt/html>。
window.requestAnimationFrame方法告诉浏览器您想要执行动画,并请求浏览器在下一次重绘之前调用指定的函数来更新动画。此方法以回调函数作为参数,在浏览器重绘之前调用。
注意:如果你想在下一次重绘中生成另一个动画屏幕,你的回调例程必须被调用
请求动画框架.
事件循环
首先了解java运行机制有助于渲染。
事件循环中有两种异步队列:宏队列和微队列。
常见的宏任务有:setTimeout、setInterval、setImmediate、(全代码)、I/O操作、UI渲染等。
常见的微任务有:process.nextTick、Promise、MutationObserver等。
示例分析:
// task是修改DOM的回调
设置超时(任务,0)
以上代码,现在任务被推入宏队列。但是因为脚本本身就是一个宏任务,所以这次执行完脚本之后,下一步就是处理微队列,然后执行一次渲染,等待下一个循环。
承诺。解决。然后(任务)
有了上面的代码,我们就完成了脚本的执行,然后去处理微任务队列?微任务处理完毕后,修改DOM,然后就可以进行渲染过程了——不再需要消耗一个额外的渲染,也不再需要等待下一轮事件循环,从而直接将最即时的更新结果呈现给用户。
上面说的重绘和回流焊,Event循环,但是很多人不知道的是,重绘和回流焊其实和Event循环有关。
当 Event loop 执行完 Microtasks 后,会判断 document 是否需要更新。因为浏览器是 60Hz 的刷新率,每 16ms 才会更新一次。然后判断是否有 resize 或者 scroll ,有的话会去触发事件,所以 resize 和 scroll 事件也是至少 16ms 才会触发一次,并且自带节流功能。判断是否触发了 media query更新动画并且发送事件判断是否有全屏操作事件执行 requestAnimationFrame 回调执行 IntersectionObserver 回调,该方法用于判断元素是否可见,可以用于懒加载上,但是兼容性不好更新界面以上就是一帧中可能会做的事情。如果在一帧中有空闲时间,就会去执行 requestIdleCallback 回调。节流防抖
当用户滚动并触发滚动事件时,用户的每次滚动都会触发我们的收听功能。函数执行消耗性能,频繁响应事件会导致大量不必要的页面计算。因此,我们需要进一步优化可能频繁触发的事件。节流防抖是必须的!
1.《前端性能优化 你必须懂的前端性能优化》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。
2.《前端性能优化 你必须懂的前端性能优化》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/tiyu/1390664.html