浏览器渲染原理及性能优化
时间:2023-06-29 10:37:00
大家好,我是前端兰峰。我已经两个多月没有更新我的博客了。在这段时间里,我正忙着找工作,加入一家新公司切都有序进行。过去的一年是一个非常特殊的一年,经历了7次.20暴雨,8月疫情在家工作一个月,以及后来的疫情,原公司12月开始长假。这些都是一言难尽的,但2022年有一个好的开始。1月入职新公司,年会抽奖中奖。然后没有留在郑州,顺利回家过年。新年伊始,我的技术博文更新将继续更新。从新年开始,我的技术博客文章将继续更新。今天,我主要与大家分享我整理的浏览器渲染原理和性能优化:性能优化是我们在工作中经常遇到的一些问题, 也是面试官经常提问的问题,希望下面文章对大家有所帮助。
一、进程与线程
- 该过程是操作系统资源分配的基本单位,包括线程
- 为了提高浏览器的稳定性和安全性,浏览器采用多过程模型
- 浏览器过程:负责界面显示、用户交互、子过程管理、提供存储等
- 渲染进程: 每个页面都有一个单独的渲染过程,其核心是渲染页面
- 网络流程:主要处理网络资源加载(html、css、js 等)
- GPU进程:3d绘制,提高性能
- 插件进程:chrome一些安装在中间的插件
二、从输入URL浏览器显示页面发生了什么?
用户输入的关键词还是URL?如果是关键字,则使用默认搜索引擎生成URL
- 用户输入url地址(关键字 根据默认引擎生成的地址,关键字将开始导航,并在浏览器过程中进行
- 浏览器进程 :会准备一个渲染进程用户渲染页面
- 在网络过程中加载资源,最终将加载资源交给渲染过程进行处理
- 渲染后显示 ipc
网络七层模型
- 检查缓存是否过期,直接返回缓存内容
- 查看域名是否被分析,DNS将域名分析成协议ip地址(DNS基于UDP)ip 端口号 host
- 请求是https SSL协商
- ip找地址,排队等候,最多可以发6个http请求
- tcp 为传输(三次握手)创建链接
- 利用tcp传输数据(分成数据包) 可靠有序,服务器按顺序接受
- http 请求(请求行 请求头 请求体)
- 默认不会断开keep-alive, 为了下次传输数据,您可以重用上次创建的链接
- 服务器收到数据后(响应行 响应头 响应体)
- 服务器返回301 302 将进行重定向操作
- 服务器返回304 查询浏览器缓存并返回
http 0.9 负责传输html 最早的时候求头和响应头
http 1.0 提供了 http的header 根据header 处理不同资源的差异
http 1.1 默认开启了keep-alive 链接复用 管线化 服务器处理多个请求(队头堵塞)
http 2.0 用同一个tcp链接发送数据 一个域名tcp(多路复用)头部压缩 服务器可以向服务器推送数据
http3.0 解决了tcp队头堵塞问题 QUIC协议 采用了udp
渲染流程
- 浏览器不能直接使用HTML,需要将HTML转化成DOM树。(document)
- 浏览器无法分析纯文本css风格,需要正确css进行解析成styleSheets.(document.styleSheets)
- 计算出DOM树中每个节点的具体风格
- 创建渲染树(布局)DOM将节点添加到布局树中,计算节点渲染到页面的坐标位置,(layout)
- 根据定位属性,通过布局树进行分层(transform属性,clip属性等)生产图层树
- 绘制不同的,转移到合成线程处理,最终生成页面并显示在浏览器上(Painting Display)
查看layer并绘制图层的列表
- css 不会阻塞html解析, 先解析html,再解析css, 样式
- css 放在底部,可能会导致重绘效果, 当html 渲染时,会先扫描js 和css 渲染从上到下进行分析和渲染
- 渲染DOM等待样式加载
- js会阻塞html解析,阻止DOM渲染 需要暂停DOM解析去执行js ,js可以操作样式,所以需要等待样式加载
所以js 页面底部一般放置,css 放头部
总结:DOM 如何生成
- 预分析操作将在分析前进行,并将提前加载js、css等文件
- 字节流 =》分词器 =》Token =》根据token 生成节点 =》插入到DOM 树中
- 遇到js 在执行过程中遇到script 标签,HTMLParse停止分析,(下载)执行相应的脚本
- 在js 在执行之前,你需要等待当前脚本上的一切css 加载分析完成(js是依赖css的加载)#
三、Perfomance API
// js window.addEventListener('DOMContentLoaded', function(){
let s = 0; for(let i = 0; i< 10000000; i ){
s =i; } setTimeout(()=>{
document.body.appendChild(document.createtextNode('hello')) },1000) }) setTimeout(()=>{
const {
fetchStart, //开始访问 requestStart, //请求的开始 responseStart, //响应的开始 responseEnd, //响应的结束 domInteractive, // dom 可交互的时间点 domContentLoadedEventEnd, //dom加载完毕 loadEventStart //所有资源加载完毕 } = performance.timing; let TTFB = responseStart - requestStart; //首字节返回的事件 服务器处理能力 let TTI = domInteractive - fetchStart; //整个的一个可交互的时间 let DCL= domContentLoadedEventEnd - fetchStart; //DOM整个加载完毕 console.log(TTFB, TTI, DCL) //如图一 const paint = performance.getEnteriesByType('paint') console.log(paint[0].startTime); //FP }, 3000) //递归看load 的时间不能为0 mutationObserver new PerformanceObserver((entryList)=>{
console.log(entryList.getEntries) }).observe({
entryTypes:['element']})
图一
图二
四、网络优化策略
- 减少HTTP请求数,合并js、css,合理内嵌js、css
- 合理设置服务器端缓存,提高服务器处理速度。(强制缓存、协商缓存)
- 避免重定向,重定向会降低响应速度(301,302)
- 使用dns-prefetch,进行DNS预解析
- 采用域名分片技术,讲资源放到不同的域名下,同一个域名最多处理6个TCP链接问题
- 采用CDN加速加快访问速度。(指派最近、高度可用)
- gzip压缩优化,对传输资源进行体积压缩(html,css,js)
Content-Encoding:gzip
- 加载数据优先级:preload(预先请求当前页面需要的资源) prefetch(将来页面中使用的资源),将数据缓存到HTTP缓存中
<link rel="preload" href="style.css" as="style">
五、关键渲染路径
- 重排(回流)Reflow:添加元素、删除元素、修改大小、移动元素位置、获取位置等相关信息都会引起重排。
- 重绘 Repaint: 页面中元素样式的改变并不影响它在文档中的位置。
1.强制同步布局问题
JavaScript强制将计算样式和布局操作提前到当前任务中
<div id="app">div>
<script>
function reflow(){
let el = documnet.getElementById('app');
let node = doucument.createElement('h1');
node.innerHTML = 'hello';
el.appendChild(node);
//获取位置会导致重排(重新布局)
console.log(app.offsetHeight);
}
window.addEventListener('load',function(){
for(let i=0; i<100; i++){
reflow()
}
})
scpript>
减少回流和重绘
- 脱离文档流
- 渲染时给图片增加固定宽高
- 尽量使用css3动画
- 可以使用will-change提取到单独的图层中
六、静态文件优化
1. 图片优化
图片格式:
- jpg:适合色彩丰富的照片、banner图;不适合图形文字、图标(纹理有锯齿),不支持透明
- png: 适合纯色、透明、图标、支持半透明;不适合色彩丰富图片,因为无损储存会导致储存体积大
- gif: 适合动画,可以动的图标;不支持半透明,不适合存储彩色图片
- webp: 适合半透明图片,可以保证图片质量和较小的体积
- svg: 相比于jpg和png,它的体积更小,渲染成本过高,适合小且色彩单一的图标
图片优化:
- 避免空src的图片
- 减小图片尺寸,节约用户流量
- img标签设置alt属性,提升图片加载失败时的用户体验
- 原生的loading:lazy图片懒加载
<img loading="lazy" src="./images/1.jpg" widht="300" height="450" />
- 不同环境下,加载不同尺寸和像素的图片
- 对于较大的图片可以考虑采用渐进式图片
- 采用base64URL 减少图片请求
- 采用雪碧图片合并图标图片
2. HTML优化
- 语义化HTML:代码简洁清晰,利于搜索引擎,便于团队开发维护
- 提前声明字符编码,让浏览器快速确定如何渲染网页内容
- 减少HTML嵌套关系,减少DOM节点数量
- 删除多余空格,空行、注释、及无用的属性等
- 避免table布局
3. css 优化
- 减少伪类选择器,减少样式层数,减少使用通配符
- 避免使用css表达式,css表达式会频繁求值,当滚动页面,或者移动鼠标时,都会重新计算(IE6,7)
background-color:expression(new Date()).getHours()%2 ? "red" : "yellow");
- 删除空行、注释、减少无意义的单位、css进行压缩
- 使用外链css,可以对css进行缓存
- 添加媒体字段,只加载有效的css 文件
"index.css" rel="stylesheet" media="screen and(min-width:1024px)"
- css contain属性将元素进行隔离
- 减少@import使用,由于import采用的串行加载
4. js 优化
- 通过async 、defer异步加载文件
- 减少DOM操作,缓存访问过的元素
- 操作不直接应用到DOM上,而应用到虚拟DOM上。最后一次性的应用到DOM上
- 使用webworker解决程序阻塞问题
- IntersectionOberver
const observer = new IntersectionOberver(function(changes){
changes.forEach(function(element, index){
if(element,intersectionRatio > 0){
observer.unobserve(element.target);
element.target.src = element.target,dataset.src;
}
});
});
function initObserve(){
const listItems = document.querySelectAll('img');
listItems.forEach(function(item){
observer.observe(item)
})
}
initObserver
- 虚拟滚动 vertual-scroll-list
- requestAnimationFrame、requestIdleCallback
![image.png](https://img-blog.csdnimg.cn/img_convert/a89b7627c824298618afa7fe8d139fdf.png
- 尽量避免使用eval,消耗时间久
- 使用事件委托,减少事件绑定个数
- 尽量使用canvas动画、css动画
七、优化策略
- 关键资源个数越多,,首次页面加载时间就会越长
- 关键资源的大小,内容越小,下载时间越短
- 优化白屏:内联css和内联js,移除文件下载较小文件体积
- 预渲染,打包时候进行预渲染
- 使用SSR加速首屏加载(耗费服务端资源),有利于SEO优化,首屏利用服务器端渲染,后续交互采用客户端渲染
八、浏览器的存储
-
cookie: cookie过期时间内一直有效,存储大小4k左右、同时限制字段个数,不适合大量的数据存储,每次请求会携带cookie,主要可以利用做身份检查验证
设置cookie有效期;根据不同子域划分cookie较少传输;静态资源域名和cookie域名采用不同域名,避免静态资源访问时携带cookie
-
localStroage: chrome下最大存储5M,除非手动清除,否则一直存在,利用localStorage存储静态资源
function cacheFile(url){
let fileContent = localStorage.getItem()
if(file){
eval(fileContent)
} else {
let xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onload = function(){
let responseText = xhr.responseText;
eval(responseText);
localStorage.setItem(url,responseText)
}
xhr.send()
}
}
cacheFile('/index.js')
- sessionStorage: 会话级别存储,可用于页面间的传值
- indexDB:浏览器的本地数据库(基本无上限)
九、增加体验 PWA(Progerssive Web App)
webapp用户体验差(不能离线访问),用户粘性低(无法保存入口),pwa就是为了解决这一系列问题让webapp具有快速,可靠,安全等特点
- Web App Mainfset: 将网站添加到桌面、更类似native的体验
- Service Worker: 离线缓存内容,配合cache API
- Push Api & Notification Api: 消息推送与提醒
- App Shell & App Skeleton App壳、骨架屏