浏览器结构
浏览器结构分为用户界面、渲染引擎、浏览器引擎。
- 用户界面用于展示除标签页窗口之外的其他用户界面内容。
- 渲染引擎负责渲染用户请求的页面内容。
- 渲染引擎下面有很多功能模块 负责网络请求的网络模块 解析和执行js的js解释器 保存数据的数据存储层 等等
- 渲染引擎是一个浏览器的核心与灵魂(也叫浏览器内核),不同浏览器使用的内核是不同的,IE使用的Trident,Firefox是Gecko,Sarafi使用的webkit, 并将其开源,Chrome是使用的基于webkit改造优化的Blink渲染引擎,也将其开源,Opera和Edge也是使用的Blink。
- 在用户界面和渲染引擎之间有个浏览器引擎,用于在用户界面和渲染引擎之间传递数据。
以Chrome为例
先拆解一下浏览器的组成结构,浏览器是运行在操作系统上的一个应用程序,应用程序启动进程执行功能 进程又去创建线程去执行
- 进程是操作系统进行资源分配的调度的基本单位,可以申请和拥有计算机资源,晋城市程序的基本执行实体
- 线程是操作系统能够进行运算调度的最小单位,一个进程进程中可以并发多个线程,每条线程执行不同的任务
当我们启动某个程序时,就会创建进程来执行任务代码 并且为他分配内存空间 当关闭应用时 该内存空间被回收 进程可以启动多个进程来执行任务,进程通过进程间的通信管道IPC来传递 一般的应用程序都是多进程的 进程之间相互独立 进程可以将任务分成更小的任务 通过创建多个线程去执行不同的任务,并且同一线程之间是可以直接通信共享数据的
浏览器就是一个多进程的结构,根据进程功能不同来拆解浏览器,可分为浏览器进程,GPU进程 插件进程 渲染器进程 缓存进程 网络进程
- 浏览器进程负责与浏览器的其他进程协调工作 用来协调各个进程部门
- 网络进程负责发起网络请求
- GPU进程负责图形渲染
- 插件进程富足控制网站使用的所有插件
- 渲染器进程用来控制显示tab标签内的所有内容 浏览器在默认情况下会为每个标签也都创建一个进程 确保来自不同站点的页面都是独立呈现的 互不影响相互独立
- 缓存进程
渲染具体的流程
1.当你在url输入地址时,浏览器进程的UI线程会捕捉你的输入内容 如果访问的是网址 则UI线程会启动一个网络线程来请求DNS进行域名解析接着开始连接服务器获取数据 如果你的输入不是网址而是一串关键字 浏览器就会知道你是搜索 于是就会使用你的默认配置的搜索引擎来查询
2.当网络线程获取到数据后 会通过SafeBrowing来检查改站点是否是恶意站点 如果是则会展示一个警告页面 阻止你的访问 可以强行访问
3.当返回数据准备完毕并且安全校验通过 网络线程会通知UI线程 UI线程会创建一个渲染器进程来渲染页面 浏览器进程通过IPC管道将数据传递给渲染器进程 正式进入渲染流程(此时地址栏的状态更新 比如history更新 此时可以点击导航懒得后退 )
4.渲染器收到的数据 也就是html 渲染器进程的核心就是把html css js img 等资源渲染成用户可交互的web页面 ,渲染器进程的主线程将html进行解析,构造DOM数据结构。 DOM文档对象模型是浏览器对其页面在其内部表现形式,是web程序员可以通过JavaScript与之交互的数据结构和API 。HTML首先经过Tokeniser标记化,在DOM 树构造过程中会创建Docunment为根节点的DOM树不断进行修改,向其中添加各种元素。(HTML代码中往往会引入一些额外的资源,比如图片,css和js脚本等。图片和css这些资源需要通过网络下载或者从缓存中直接加载。这些资源不会阻塞html的解析,因为他们不会影响DOM的生成,但当html解析过程中遇到script标签,将停止html解析流程,转而去加载解析并且执行js。你可能就会问了?为什么不直接跳过js的加载和执行这一过程,等html解析完后再加载运行js呢?这是因为,浏览器不知道js的执行是否会改变当前页面的html的结构,如果js代码了调用document.write方法来修改html,那之前的html的解析就没有任何意义了。这也就是为什么我们一直说要把script标签要放在合适的位置,或者使用async 或defer属性来异步加载执行js。)
5.当html解析完,我们就获得了一个 dom tree,但我们还不知道dom tree上每个节点应该长什么样子,在知道dom结构和每个节点的样式后,我们需要知道每个节点需要放在页面上的哪个位置,以及该节点需要占用多大的区域。这个阶段被称为layout布局,layout tree是和最后展示在屏幕上的节点对应的
6.现在,我们知道了元素的大小,形状,和位置。这还不够,还需要知道是以什么顺序绘制各个节点的(z-index这个属性就会影响节点绘制的层级关系),为了保证在屏幕上展示正确的层级,在绘制阶段,主线程遍历layout tree 创建了一个绘制记录表,该表记录绘制的顺序。
7.也知道了文档的绘制顺序了,然后主线程将这些信息传递给compositor线程。合成器线程将每个图层栅格化(一层可能像页面的整个长度一样大,因此合成器线程将他们分为多个土块,然后将每个图块发送给栅格线程)。栅格线程栅格化每个图块并将他们存储到GPU内存中,对图块进行栅格化后,合成器线程可以给不同的栅格线程分别优先级。当图块栅格化完成后,合成器线程将手机称为‘draw quads’的图块化信息,根据这些数据合成器线程生成一个合成器Frame。然后这个合成器frame通过IPC传送给浏览器进程,接着浏览器进程将compositor frame 传到GPU,然后GPU渲染展示到屏幕上,则会生成一个新的compositor feame ,新的frame再传给GPU。再次渲染到屏幕上。
总结一下:
浏览器进程的网络线程请求获取到html数据和通过IPC将数据传给渲染器进程的主线程,主线程讲html解析构造DOM树,然后计算样式,根据DOM树和样式生成layout Tree,通过遍历layout tree生成绘制顺序表,然后主线程将layout Tree和绘制顺序信息一起传给合成器线程,合成器线程按规则进程分图层,并把图层分为更小的图块传给栅格线程进行栅格化,栅格化完成后,合成器线程会获得栅格线程传过来的"draw quads"图块信息,根据这些信息,合成器线程合成了一个frame,然后将该合成frame通过IPC传回给浏览器进程,浏览器进程在传到GPU进行渲染,最后就展示到你的屏幕上了。
原文章来自微信公众号:Obj.Tube