浏览器原理相关知识整理
详解输入url到渲染出页面全过程
简单来讲,从输入url到渲染浏览器将经历以下过程:
- 处理用户输入
- URL请求流程
- 准备渲染阶段
- 提交文档
- 渲染阶段
处理输入
- 浏览器进程接收用户输入,进行url解析,判断url是否符合规则,不符合则判定为搜索内容,调用搜索引擎进行搜索;
- 用户在地址栏输入内容回车后,在即将离开当前页面时,当前页面还有一次执行beforeunload的机会,用于执行数据清理,询问用户等操作,此过程可取消导航到新页面并退出流程
- 浏览器进程将url通过IPC(进程间通信)转发给网络进程,标签页图标会进入加载状态,等到新的url返回后,页面内容才会被替换
URL请求流程
网络进程拿到url后进入url请求流程
- 检查浏览器缓存,若命中且有效,则直接返回数据并退出流程
- DNS解析
- 建立TCP连接,和TLS连接
- 发送http请求
- 服务器返回响应数据
- 根据响应码(成功、重定向、错误等情况)和响应头中的content-type处理响应数据,若响应为content-type:text/html,浏览器进程会让当前页面执行清理操作visibilitychange, beforeunload事件,清理结束后进入下一步流程
准备渲染阶段
- chrome分配渲染进程
- 通常情况下每个新建页面分配一个渲染进程
- 但从一个页面打开了另一个页面,且两页面同属于一个站点(协议和根域名相同)时会复用父页面进程
提交文档阶段
浏览器进程将网络进程返回的html文档数据提交给渲染进程的过程叫提交文档阶段
- 浏览器进程收到网络进程的响应头数据后,向渲染进程发起“提交文档”消息
- 渲染进程收到消息后与网络进程建立管道连接,边传输数据边解析HTML,构建DOM树
- 文档数据传输完成后渲染进程返回“确认提交消息”给浏览器进程
- 浏览器收到消息后更新浏览器界面状态:安全状态、地址栏、前进后退历史状态,并更新web页面
渲染阶段
此阶段中渲染进程开始页面解析和子资源加载,页面生成后渲染进程发送消息给浏览器进程停止图标上的加载动画
- 构建DOM树
- 便于浏览器理解与渲染;给js提供接口;过滤不安全的内容;
- 通过渲染引擎的html解析器模块,进行流式的AST解析生成
- 构建CSSOM树
- CSSOM对应DOM中的document.styleSheets
- 过程中大体与DOM解析类似,包含link,style标签以及元素的style属性,将非标准值(em,blue等)标准化(px,
rgb()
)
- 结合DOM树和CSSOM树生成布局树
- 遍历DOM树中所有可见节点,并添加到布局中,计算节点的坐标位置
- 分层
- 基于布局树为层叠上下文及需要裁剪的地方(overflow:auto需要滚动)创建图层树
- 图层树以内存管理为代价提升了DOM更新的性能,不宜过度使用
- 图层绘制
- 渲染引擎将图层拆分为小的绘制指令,生成待绘制指令列表
- 合成(切分图块和栅格化)
- 主线程将绘制列表交给合成线程;为避免不可见部分的渲染开销,合成线程将图层切成小图块(tile)
- 合成线程将视口附近的的图块交由栅格化线程生成位图
- 通常栅格化过程由gpu加速,生成位图保存在gpu内存中
- 所有图块栅格化完成后,合成块生成'drawQuad'命令,通知浏览器进程
- 显示
- 浏览器进程中viz组件收到命令后将页面绘制到内存中并显示在显示器上
首次渲染与页面阻塞
- 预加载扫描器会在后台预先请求资源,不需要等解析到外链CSS、JavaScript文件所在行才去请求资源
- 外链CSS文件的加载,不会阻塞其后HTML内容的解析,但会阻塞其后结构的渲染,之前的不受影响
- 外链CSS文件的加载,会阻塞其后外链JavaScript文件的执行,从而间接阻塞HTML解析
- 外链js文件的加载和执行都会阻塞后续html解析,其之前的结构会进行首次渲染
- 外链js文件加载或被css阻塞会暂存并中断渲染task,继续执行event loop,并渲染之前未被阻塞的不完整的内容,若仅仅是执行阻塞,则不会中断渲染task,等待脚本执行完成后再渲染
因为历史原因js经常在执行中使用document.write导致无法安全的渲染节点,因此浏览器选择让js阻塞,现代浏览器给script标签增加了async或defer属性提供非阻塞支持 async和defer的特性见文档
html文档加载完毕,且内联和外链js加载执行完毕后触发DOMContentLoaded事件
html中的资源以及js代码中异步加载的静态资源加载完成后触发load事件
一些优化措施:
- 预解析DNS:
<link rel="dns-prefetch" href="https://blog.windstone.cc">
- 预建立 TCP 连接:
<link rel="preconnect" href="https://blog.windstone.cc">
浏览器多进程和js单线程
现代浏览器是多进程的,大多数单个tab页面相当于一个独立的浏览器进程
浏览器主要包含的进程有:
- 浏览器主进程:仅有一个负责协调,页面管理,创建或销毁其他进程
- 渲染进程:将资源转换为可交互的网页,每个tab一个,运行在沙箱模式下,包含如下线程
- 预解析线程(预加载扫描)
- 页面渲染(GUI线程,Blink等),repaint或reflow等时机执行
- 脚本执行线程(js引擎,V8等),等待任务队列并执行,与渲染线程互斥执行
- 事件处理,事件触发线程,控制事件循环,事件触发时将回调放入js引擎任务队列队尾
- 定时计时的定时器线程,setTimeout和setInterval所在线程,负责计时
- 异步请求(异步 HTTP 请求线程):http请求连接后新开的线程,当状态变更时将回调放入js引擎任务队列中
- IO 线程: 负责与其他进程 IPC 进行通信,比如接收用户输入事件、网络事件、设备相关等事件
- 光栅化线程(池)
- 合成线程
- 网络进程:负责网络资源加载,所有tab共有
- GPU进程:最多一个,用于3D绘制。所有tab共有
- 插件进程:负责插件运行,每种类型的插件对应一个
web worker能开辟一个js执行子线程,并通过postMessage API与js引擎线程通信