ZLL的每周一更 介绍浏览器分类和介绍浏览器的结构浏览器中的进程和线程浏览器解析流程最后
介绍
你好! 这是ZLL每周一更的第一篇内容,每周我都会整理关于前端的一些知识点,帮助大家(以及我自己,^^)在工作以及求职的路上更加顺利,这就是这个系列的开篇啦,在开篇的介绍里面肯定不得已要说多一点。好了,来到正题,这周的内容是浏览器的幕后原理,参考了MDN以及Tali Garsiel && Paul Irish编写的这篇文章,希望能争取把知识用更好理解的方式展示给各位,那么就开始啦。
浏览器分类和介绍
目前使用的主流浏览器有五个:Internet Explorer、Firefox、Safari、Chrome 浏览器和 Opera。本文中以开放源代码浏览器为例,即 Firefox、Chrome 浏览器和 Safari(部分开源)。根据 StatCounter 浏览器统计数据,目前(2011 年 8 月)Firefox、Safari 和 Chrome 浏览器的总市场占有率将近 60%。由此可见,如今开放源代码浏览器在浏览器市场中占据了非常坚实的部分。
浏览器的结构
浏览器自上至下分为这几层,分别是
1. 用户界面 – 包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗口显示的您请求的页面外,其他显示的各个部分都属于用户界面。
2. 浏览器引擎 – 在用户界面和呈现引擎之间传送指令。
3. 呈现引擎 – 负责显示请求的内容。如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。
4. 网络 – 用于网络调用,比如 HTTP 请求。其接口与平台无关,并为所有平台提供底层实现。
5. 用户界面后端 – 用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。
6. JavaScript 解释器。用于解析和执行 JavaScript 代码。
7. 数据存储。这是持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie。新的 HTML 规范 (HTML5)定义了“网络数据库”,这是一个完整(但是轻便)的浏览器内数据库。
以上是浏览器的主要架构,但是和我们日常浏览器的使用还是有区别,我们当前所用的浏览器(例如谷歌)是多进程的,那么以上浏览器结构中哪些是需要开启多进程哪些是只有一个进程处理的呢,首先我们就得来谈谈浏览器中的进程和线程
浏览器中的进程和线程
首先,我们得弄清楚进程和线程的概念
1:进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位
2:线程是拥有资源和独立运行的最小单位,也是程序执行的最小单位
类似于下图,简而言之,进程就相当于一个工厂,线程就相当于工厂中的工人,工厂中的工人(线程)可以共享工厂(进程)中的资源,但是不同工厂(进程)之间不能进行资源共享。
在弄清楚之后,我们就来看看浏览器中到底执行了哪些进程和哪些线程
浏览器中的进程
1:浏览器的主进程(负责协调、主控),只有一个
2:第三方插件进程(每种类型的插件对应一个进程,仅当使用该插件时才创建)
3:GPU进程(最多一个,用于3D绘制)
4:浏览器渲染进程 默认每个Tab页面一个进程,互不影响,控制页面渲染,脚本执行,事件处理等(有时候会优化,如多个空白tab会合并成一个进程)
(以下图片中,用户界面,浏览器引擎,网络层,用户界面后端都属于浏览器主进程)
渲染进程中的线程
基本上分为以下五大线程
1. GUI渲染线程
(GUI渲染进程与上文的GPU进程不是一个…)
负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
2. JS引擎线程
也称为JS内核,负责处理Javascript脚本程序。(例如V8引擎)
JS引擎线程负责解析Javascript脚本,运行代码。
JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序
同样注意,GUI渲染线程与JS引擎线程是互斥的,所以如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
3. 事件线程
归属于浏览器而不是JS引擎,用来控制事件循环(可以理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)
当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其他线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中
当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理
注意,由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)
4. 定时器线程
传说中的setInterval与setTimeout所在线程
浏览器定时计数器并不是由JavaScript引擎计数的,(因为JavaScript引擎是单线程的,
如果处于阻塞线程状态就会影响记计时的准确)
因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)
注意,W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。
5. 异步HTTP线程
在XMLHttpRequest在连接后是通过浏览器新开一个线程请求(通过浏览器网络模块)
将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由JavaScript引擎执行。
浏览器解析流程 首先,渲染进程通过浏览器从网络层获取请求文档内容,然后便开始解析HTML文件,将其中的DOM和CSS文件分别解析成DOM树和CSS规则树,然后共同组成渲染树,(注意DOM树与渲染树的节点不一定相同,例如display:none的DOM节点则不会出现在渲染树中)下一步进入“布局”处理阶段,也就是为每个节点分配一个应出现在屏幕上的确切坐标,也就是我们熟知的重排下一个阶段是绘制 – 呈现引擎会遍历呈现树,由用户界面后端层将每个节点绘制出来,也就是我们熟知的重绘。
所以很容易理解,重排必定会导致重绘,而重绘不会导致重排。
如图,这是Webkit浏览器解析的主流程
在浏览器解析的过程中,DOM节点的解析与CSS节点的解析是同步进行的,之间并不会阻塞,但是一旦涉及到JS的解析,就会导致阻塞,具体规则是这样的:
1:DOM节点的解析遇到JS时,则会挂起当前DOM的解析过程,直到JS请求和执行完毕后再继续DOM的解析
2:CSS资源的请求解析遇到JS时,则会阻塞当前的JS,直到CSS资源加载和构建完毕再开始JS的执行(CSS构建styleSheet的时间非常短,可以忽略不计)
*原因如下:脚本在文档解析阶段会请求样式信息。如果当时还没有加载和解析样式,脚本就会获得错误的回复,这样显然会产生很多问题。这看上去是一个非典型案例,但事实上非常普遍。Firefox 在样式表加载和解析的过程中,会禁止所有脚本。而对于 WebKit 而言,仅当脚本尝试访问的样式属性可能受尚未加载的样式表影响时,它才会禁止该脚本 *
最后
本文只是让大家对于浏览器的运行和解析机制有基本的了解,如果大家想深入了解浏览器内部的运行机制,且后期可能会根据自身理解更新已更新内容,例如DOM如何解析成DOM树,浏览器解析器的运行规则,十分推荐大家去阅读这篇文章,浏览器的工作原理,对于文中有错误的点也欢迎大家积极指出,咱们下周见!