阅读 236

浏览器渲染原理与性能优化

一、浏览器渲染原理


image.png

  • 浏览器进程:浏览器的主进程,负责界面展示、用户交互、子进程管理、存储管理等。
  • GPU进程:3d绘制,开启硬件加速,提高用户体验。
  • 插件进程:管理浏览器插件的运行。
  • 网络进程:处理网络资源。
  • 渲染进程:用于渲染页面。

一道经典问题:从用户输入url回车,发生了什么?
从浏览器进程的角度来看:

  1. 用户输入url地址并回车,浏览器进程会根据默认的引擎生成地址,并开始导航。
  2. 网络进程加载资源,加载完毕后,交给渲染进程渲染页面。
  3. 渲染进程来渲染页面。

网络七层模型:

  1. 物理层(比特流):物理寻址,比特传输。
  2. 数据链路层(数据帧,加帧头帧尾):节点到节点的数据传输;组帧、物理寻址、流量控制、差错控制。
  3. 网络层(数据报,加ip):源主机到目的主机数据分组交付。逻辑寻址、路由、分组转发。
  4. 传输层(数据段):端到端进程间完整报文传输。SAP寻址,端到端连接、流量、差错控制。
  5. 会话层、表示层、应用层:会话控制、编解码加减密、报文。

http协议发展历史:

  1. http 0.9 负责传输html,最早没有请求头和响应头,浏览器直接处理字符串。
  2. http 1.0 提供了http的header,根据header的不同来处理不同的资源。
  3. http 1.1 默认开启了keep-alive 链路复用、管线化(并发发送6个请求)。服务器响应式安顺序,存在队头阻塞的问题。
  4. http 2.0 多路复用(同一个tcp连接发送数据)、头部压缩(冒号)、二进制传输、服务端推送数据。
  5. http 3.0 解决了tcp的队头阻塞问题,采用udp而不是tcp,在udp上层增加了QUIC协议。

流程梳理:

  1. DNS域名解析,域名解析为ip地址。
  2. 缓存查找,检查缓存是否过期。
  3. 请求是https,进行SSL协商。
  4. ip地址进行寻址,排队等待,最多能发送6个http请求。
  5. tcp创建连接,三次握手。
  6. 利用tcp传输数据(拆包,分组,有序,可靠传输)服务器会按照顺序来接受。
  7. 发起http请求(请求行、请求头、请求体)
  8. 默认不会断开keep-alive,保证下次传输数据是,可以服用上次创建的连接。
  9. 服务器收到数据后(响应行,响应头,响应体)
  10. 服务器返回301,302会进行重定向
  11. 服务器返回304,查询浏览器缓存进行返回

浏览器接受资源后怎么处理呢?(渲染流程)
6263294-50dd4f2acfb514b2.webp

  1. 浏览器无法识别HTML,需要自上而下解析,将html转化成DOM树。(document)
  2. 浏览器无法解析纯文本的CSS样式,需要对CSS进行解析,解析成styleSheets(CSSOM)。(document.styleSheets)
  3. 计算出DOM树中每个节点的具体样式。(Attachment)
  4. 创建渲染(布局)树,将DOM树种可见节点,添加到布局树中。并计算节点渲染到页面的坐标位置。(layout)
  5. 通过布局树,进行分层(根据定位属性、透明属性、transform属性、clip属性等)生成图层树。
  6. 将不同图层进行绘制,转交给合成线程处理。最终生成页面并显示在浏览器上(Painting,Display)。

一道经典问题:为什么JS放底部CSS放顶部
HTML自上而下进行解析,遇到header里引用外部样式,不会立即去加载CSS样式,而是继续解析HTML,HTML解析完成后,再去解析CSS,等待CSS解析完成后,进行渲染。(CSS不会阻塞HTML解析)

样式放在底部,而不是head里可能会导致重绘回流。因为样式不放在head里,放在下面的话,浏览器渲染DOM并不会等待CSS加载解析完成后才触发,而是在在解析HTML的时候会先绘制一次,解析到CSS以后,又再次绘制。

JS的执行会阻塞DOM解析,同时阻塞DOM渲染。JS要等待上面的CSS加载完毕,保证页面js可以操作样式。

一道经典问题:DOM如何生成,渲染到页面上的
browerrender.jpeg node模拟浏览器发送http请求,接收响应后进行html和css解析【gitee地址】

二、Perfomance API


534179-20151027144043654-498773427.png

关键时间节点描述含义
TTFBtime to first byte(首字节时间)从请求到数据返回第一个字节所消耗时间
TTITime to Interactive(可交互时间)DOM树构建完毕,代表可以绑定事件
DCLDOMContentLoaded (事件耗时)当 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发
LonLoad (事件耗时)当依赖的资源全部加载完毕之后才会触发
FPFirst Paint(首次绘制)第一个像素点绘制到屏幕的时间
FCPFirst Contentful Paint(首次内容绘制)首次绘制任何文本,图像,非空白节点的时间
FMPFirst Meaningful paint(首次有意义绘制)首次有意义绘制是页面可用性的量度标准
LCPLargest Contentful Paint(最大内容渲染)在viewport中最大的页面元素加载的时间
FIDFirst Input Delay(首次输入延迟)用户首次和页面交互(单击链接,点击按钮等)到页面响应交互的时间
performance.timing API含义
navigationStart在同一个浏览器上下文中,前一个网页(与当前页面不一定同域)unload 的时间戳,如果无前一个网页 unload ,则与 fetchStart 值相等
unloadEventStart前一个网页(与当前页面同域)unload 的时间戳,如果无前一个网页 unload 或者前一个网页与当前页面不同域,则值为 0
redirectStart第一个 HTTP 重定向发生时的时间。有跳转且是同域名内的重定向才算,否则值为 0
redirectEnd最后一个 HTTP 重定向完成时的时间。有跳转且是同域名内的重定向才算,否则值为 0
fetchStart浏览器准备好使用 HTTP 请求抓取文档的时间,这发生在检查本地缓存之前
domainLookupStartDNS 域名查询开始的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等
domainLookupEndDNS 域名查询完成的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等
connectStartHTTP(TCP) 开始建立连接的时间,如果是持久连接,则与 fetchStart 值相等,如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接开始的时间
secureConnectionStartHTTPS 连接开始的时间,如果不是安全连接,则值为 0
connectEndHTTP(TCP) 完成建立连接的时间(完成握手),如果是持久连接,则与 fetchStart 值相等,如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接完成的时间
requestStartHTTP 请求读取真实文档开始的时间(完成建立连接),包括从本地读取缓存,连接错误重连时,这里显示的也是新建立连接的时间
responseStartHTTP 开始接收响应的时间(获取到第一个字节),包括从本地读取缓存
responseEndHTTP 响应全部接收完成的时间(获取到最后一个字节),包括从本地读取缓存
domLoading开始解析渲染 DOM 树的时间,此时 Document.readyState 变为 loading,并将抛出 readystatechange 相关事件
domInteractive完成解析 DOM 树的时间,Document.readyState 变为 interactive,并将抛出 readystatechange 相关事件
domContentLoadedEventStartDOM 解析完成后,网页内资源加载开始的时间,代表DOMContentLoaded事件触发的时间节点
domContentLoadedEventEndDOM 解析完成后,网页内资源加载完成的时间(如 JS 脚本加载执行完毕)
domCompleteDOM 树解析完成,且资源也准备就绪的时间,Document.readyState 变为 complete,并将抛出 readystatechange 相关事件
loadEventStartload 事件发送给文档,也即 load 回调函数开始执行的时间,如果没有绑定 load 事件,值为 0
loadEventEndload 事件的回调函数执行完毕的时间,如果没有绑定 load 事件,值为 0

白屏时间:domLoading - navigateStart
首屏时间:domInteractive - navigateStart DNS查询耗时: domainLookupEnd - domainLookupStart
TCP链接耗时: connectEnd - connectStartrequest
请求耗时: responseEnd - responseStart
解析dom树耗时: domComplete - domInteractive
domready时间: domContentLoadedEventEnd - fetchStart onload时间 = loadEventEnd - fetchStart

performance API 实例【gitee地址】

三、网络优化策略


  • 减少http请求数,合理内嵌CSS,JS。
  • 合理设置服务端缓存,提高服务器处理速度(强缓存、协商缓存)。
  • 避免重定向,重定向会降低响应速度。
  • 使用dns-prefetch,进行DNS预解析。
  • 采用域名分片技术,将资源放到不同的域名下。(一个域名最多处理6个TCP连接请求)。
  • 采用CDN加快访问速度。
  • gzip压缩优化,对传输资源进行体积优化(html,js,css)图片视频不用做gzip。
  • 加载资源优先级。preload(预先请求当前页面需要的资源) prefetch(将来页面中使用的资源) 将数据缓存到HTTP缓存中。
<link rel="preload" href="style.css" as="style">
复制代码

四、关键渲染路径


关键渲染路径,指只要渲染,就会执行这些流程。

视觉发生变化 =》 样式计算 =》 布局(重排) =》绘制(重绘)=》 合成渲染

  • 重排:添加、删除元素,修改元素大小,移动元素位置,获取元素位置信息
  • 重绘:更改元素样式,不改变元素在文档流中的样式。

如何减少重排重绘:

  1. 脱离文档流。
  2. 图片指定宽高。
  3. 尽量使用css动画。(只绘制一次),可以开启GUP加速。
  4. 使用will-change提取到单独的图层,浏览器会单独留意它。

五、静态文件优化


1. 图片优化

图片格式:

  • jpg
  • png
  • gif
  • webp
  • svg

图片优化:

  • 避免空src的图片,也会发送请求。
  • 减小图片尺寸,压缩图片,减少用户流量。
  • img标签加上alt属性,图片加载失败时用户体验。
  • 较大图片采用渐进式图片。
  • 小图标base64减少图片请求。
  • 原生的图片懒加载:loading:lazy
<img loading="lazy" src="./images/1.jpg" width="300" height="450" />
复制代码
  • 不同环境下,加载不同尺寸和像素的图片
<img src="./images/1.jpg" sizes="(max-width:500px) 100px,(max-width:600px) 200px"  srcset="./images/1.jpg 100w, ./images/3.jpg 200w">
复制代码

2. html优化

  • 语义化html:利于搜索引擎,便于团队开发。
  • 提前声明字符集,让浏览器知道如何渲染网页内容。
  • 减少无意义dom标签。
  • 减少使用iframe,iframe会阻塞onload事件。可以动态加载iframe。
  • 避免使用table布局,table布局是整个加载完了以后才渲染。

3. CSS优化

  • 减少伪类选择器,减少样式层数,减少使用通配符。
  • 避免使用css表达式,css表达式会频繁求值。
  • 删除空行、注释、无意义的单位
  • css压缩。
  • 使用外链,可以对css进行缓存。
  • css contain属性,将元素进行隔离。
  • 减少使用@import,它是采用的是穿行加载。

4. JS优化

  • async、defer异步加载文件。

test.3ca4a381.png

  • 减少DOM操作,缓存访问过的元素。
  • webworker解决程序阻塞问题。(复杂计算)
  • IntersectionObserver,监控可视区范围。
const observer = new IntersectionObserver(function(changes) { 
    changes.forEach(function(element, index) {
        if (element.intersectionRatio > 0) {
            observer.unobserve(element.target);
            element.target.src = element.target.dataset.src;
        }
    });
});
function initObserver() {
    const listItems = document.querySelectorAll('img');
    listItems.forEach(function(item) {
        observer.observe(item);
    });
}
initObserver();
复制代码
  • 虚拟滚动 vertual-scroll-list
  • requestAnimationFrame, requestIdleCallback
  • 使用事件委托,减少事件绑定个数。
  • 使用canvas动画、CSS动画。

六、优化策略


  1. 资源加载数量
  2. 资源体积大小
  3. 资源预加载
  4. 服务端渲染,SEO。
  5. 接口请求数量
  6. 缓存的使用

八、PWA(Progressive Web App)


webapp不能离线访问,用户无法保存入口。

  • Web App Minifest:将网站添加到桌面。
  • Service Worker: 离线缓存内容,配合cache API。
  • Push API & Notification Api:消息推送。
  • App Shell & App Skeleton:App壳,骨架屏。

九、LightHouse使用


npm install lighthouse -g
lighthouse --view http://www.baidu.com
复制代码
文章分类
前端
文章标签