一、浏览器渲染原理
- 浏览器进程:浏览器的主进程,负责界面展示、用户交互、子进程管理、存储管理等。
- GPU进程:3d绘制,开启硬件加速,提高用户体验。
- 插件进程:管理浏览器插件的运行。
- 网络进程:处理网络资源。
- 渲染进程:用于渲染页面。
一道经典问题:从用户输入url回车,发生了什么?
从浏览器进程的角度来看:
- 用户输入url地址并回车,
浏览器进程会根据默认的引擎生成地址,并开始导航。 网络进程加载资源,加载完毕后,交给渲染进程渲染页面。渲染进程来渲染页面。
网络七层模型:
- 物理层(比特流):物理寻址,比特传输。
- 数据链路层(数据帧,加帧头帧尾):节点到节点的数据传输;组帧、物理寻址、流量控制、差错控制。
- 网络层(数据报,加ip):源主机到目的主机数据分组交付。逻辑寻址、路由、分组转发。
- 传输层(数据段):端到端进程间完整报文传输。SAP寻址,端到端连接、流量、差错控制。
- 会话层、表示层、应用层:会话控制、编解码加减密、报文。
http协议发展历史:
- http 0.9 负责传输html,最早没有请求头和响应头,浏览器直接处理字符串。
- http 1.0 提供了http的header,根据header的不同来处理不同的资源。
- http 1.1 默认开启了keep-alive 链路复用、管线化(并发发送6个请求)。服务器响应式安顺序,存在队头阻塞的问题。
- http 2.0 多路复用(同一个tcp连接发送数据)、头部压缩(冒号)、二进制传输、服务端推送数据。
- http 3.0 解决了tcp的队头阻塞问题,采用udp而不是tcp,在udp上层增加了QUIC协议。
流程梳理:
- DNS域名解析,域名解析为ip地址。
- 缓存查找,检查缓存是否过期。
- 请求是https,进行SSL协商。
- ip地址进行寻址,排队等待,最多能发送6个http请求。
- tcp创建连接,三次握手。
- 利用tcp传输数据(拆包,分组,有序,可靠传输)服务器会按照顺序来接受。
- 发起http请求(请求行、请求头、请求体)
- 默认不会断开keep-alive,保证下次传输数据是,可以服用上次创建的连接。
- 服务器收到数据后(响应行,响应头,响应体)
- 服务器返回301,302会进行重定向
- 服务器返回304,查询浏览器缓存进行返回
浏览器接受资源后怎么处理呢?(渲染流程)
- 浏览器无法识别HTML,需要自上而下解析,将html转化成DOM树。(document)
- 浏览器无法解析纯文本的CSS样式,需要对CSS进行解析,解析成styleSheets(CSSOM)。(document.styleSheets)
- 计算出DOM树中每个节点的具体样式。(Attachment)
- 创建渲染(布局)树,将DOM树种可见节点,添加到布局树中。并计算节点渲染到页面的坐标位置。(layout)
- 通过布局树,进行分层(根据定位属性、透明属性、transform属性、clip属性等)生成图层树。
- 将不同图层进行绘制,转交给合成线程处理。最终生成页面并显示在浏览器上(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如何生成,渲染到页面上的
node模拟浏览器发送http请求,接收响应后进行html和css解析【gitee地址】
二、Perfomance API
| 关键时间节点 | 描述 | 含义 |
|---|---|---|
| TTFB | time to first byte(首字节时间) | 从请求到数据返回第一个字节所消耗时间 |
| TTI | Time to Interactive(可交互时间) | DOM树构建完毕,代表可以绑定事件 |
| DCL | DOMContentLoaded (事件耗时) | 当 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发 |
| L | onLoad (事件耗时) | 当依赖的资源全部加载完毕之后才会触发 |
| FP | First Paint(首次绘制) | 第一个像素点绘制到屏幕的时间 |
| FCP | First Contentful Paint(首次内容绘制) | 首次绘制任何文本,图像,非空白节点的时间 |
| FMP | First Meaningful paint(首次有意义绘制) | 首次有意义绘制是页面可用性的量度标准 |
| LCP | Largest Contentful Paint(最大内容渲染) | 在viewport中最大的页面元素加载的时间 |
| FID | First Input Delay(首次输入延迟) | 用户首次和页面交互(单击链接,点击按钮等)到页面响应交互的时间 |
| performance.timing API | 含义 |
|---|---|
| navigationStart | 在同一个浏览器上下文中,前一个网页(与当前页面不一定同域)unload 的时间戳,如果无前一个网页 unload ,则与 fetchStart 值相等 |
| unloadEventStart | 前一个网页(与当前页面同域)unload 的时间戳,如果无前一个网页 unload 或者前一个网页与当前页面不同域,则值为 0 |
| redirectStart | 第一个 HTTP 重定向发生时的时间。有跳转且是同域名内的重定向才算,否则值为 0 |
| redirectEnd | 最后一个 HTTP 重定向完成时的时间。有跳转且是同域名内的重定向才算,否则值为 0 |
| fetchStart | 浏览器准备好使用 HTTP 请求抓取文档的时间,这发生在检查本地缓存之前 |
| domainLookupStart | DNS 域名查询开始的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等 |
| domainLookupEnd | DNS 域名查询完成的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等 |
| connectStart | HTTP(TCP) 开始建立连接的时间,如果是持久连接,则与 fetchStart 值相等,如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接开始的时间 |
| secureConnectionStart | HTTPS 连接开始的时间,如果不是安全连接,则值为 0 |
| connectEnd | HTTP(TCP) 完成建立连接的时间(完成握手),如果是持久连接,则与 fetchStart 值相等,如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接完成的时间 |
| requestStart | HTTP 请求读取真实文档开始的时间(完成建立连接),包括从本地读取缓存,连接错误重连时,这里显示的也是新建立连接的时间 |
| responseStart | HTTP 开始接收响应的时间(获取到第一个字节),包括从本地读取缓存 |
| responseEnd | HTTP 响应全部接收完成的时间(获取到最后一个字节),包括从本地读取缓存 |
| domLoading | 开始解析渲染 DOM 树的时间,此时 Document.readyState 变为 loading,并将抛出 readystatechange 相关事件 |
| domInteractive | 完成解析 DOM 树的时间,Document.readyState 变为 interactive,并将抛出 readystatechange 相关事件 |
| domContentLoadedEventStart | DOM 解析完成后,网页内资源加载开始的时间,代表DOMContentLoaded事件触发的时间节点 |
| domContentLoadedEventEnd | DOM 解析完成后,网页内资源加载完成的时间(如 JS 脚本加载执行完毕) |
| domComplete | DOM 树解析完成,且资源也准备就绪的时间,Document.readyState 变为 complete,并将抛出 readystatechange 相关事件 |
| loadEventStart | load 事件发送给文档,也即 load 回调函数开始执行的时间,如果没有绑定 load 事件,值为 0 |
| loadEventEnd | load 事件的回调函数执行完毕的时间,如果没有绑定 load 事件,值为 0 |
白屏时间:domLoading - navigateStart
首屏时间:domInteractive - navigateStart
DNS查询耗时: domainLookupEnd - domainLookupStart
TCP链接耗时: connectEnd - connectStartrequest
请求耗时: responseEnd - responseStart
解析dom树耗时: domComplete - domInteractive
domready时间: domContentLoadedEventEnd - fetchStart
onload时间 = loadEventEnd - fetchStart
三、网络优化策略
- 减少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">
四、关键渲染路径
关键渲染路径,指只要渲染,就会执行这些流程。
视觉发生变化 =》 样式计算 =》 布局(重排) =》绘制(重绘)=》 合成渲染
- 重排:添加、删除元素,修改元素大小,移动元素位置,
获取元素位置信息。 - 重绘:更改元素样式,不改变元素在文档流中的样式。
如何减少重排重绘:
- 脱离文档流。
- 图片指定宽高。
- 尽量使用css动画。(只绘制一次),可以开启GUP加速。
- 使用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异步加载文件。- 减少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动画。
六、优化策略
- 资源加载数量
- 资源体积大小
- 资源预加载
- 服务端渲染,SEO。
- 接口请求数量
- 缓存的使用
八、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