【进阶】面试官问我 Chrome 浏览器的渲染原理(6000 字长文)
哪吒人生信条:如果你所学的东西 处于喜欢 才会有强大的动力支撑。
每天学习编程,让你离梦想更新一步,感谢不负每一份热爱编程的程序员,不论知识点多么奇葩,和我一起,让那一颗四处流荡的心定下来,一直走下去,加油,2021
加油!欢迎关注加我vx:xiaoda0423
,欢迎点赞、收藏和评论
不要害怕做梦,但是呢,也不要光做梦,要做一个实干家,而不是空谈家,求真力行。
前言
对于 HTML,css 和 JavaScript 是如何变成页面的,这个问题你了解过吗?浏览器究竟在背后都做了些什么事情呢?让我们去了解浏览器的渲染原理,是通往更深层次的开发必不可少的事情,能让我们更深层次,多角度的去考虑性能优化等问题。
HTML,css,JavaScript 数据经过中间渲染模块的处理,最终显示在页面上(其中 HTML 超文本标记语言,CSS 层叠样式表,JS 为 JavaScript,大家一般都知道是什么,写过网页的朋友,学习者大都知道的)。
HTML 的内容是由标记和文本组成
CSS 称为层叠样式表,是由选择器和属性组成
JS 是可以使网页的内容“动”起来
有人说渲染流程可以分为:构建 DOM 树,样式计算,布局阶段,分层,绘制,分块,光栅化和合成等。其中浏览器复习一下,它是多线程的的,js 是单线程的,JS 在浏览器中,它可以是多线程的。
下面围绕浏览器渲染原理话题开始描述,为什么要了解浏览器渲染页面机制呢?。
浏览器渲染原理
首先,JavaScript 引擎是基于事件驱动单线程执行的,渲染线程负责渲染浏览器界面,但是 GUI 渲染线程与 JS 引擎是互斥的,当 JS 引擎执行时 GUI 线程会被挂起,GUI 的更新也会被保存在一个队列中,等到 JS 引擎空闲时才有机会被执行。
那么什么是 DOCTYPE 以及作用呢
DTD,document type definition, 文档类型定义,是一系列的语法规则,用来定义 XML 或(x)HTML 的文件类型。浏览器会使用它来判断文档类型,决定使用何种协议来解析,以及切换浏览器模式。
DOCTYPE 是用来声明文档类型和 DTD 规范的,一个主要的用途便是文件的合法性验证。如果文件代码不合法,那么浏览器解析时便会出一些差错。
下图为浏览器的渲染过程图:
重排 Reflow
重排的定义:DOM 结构中的各个元素都有自己的盒子模型,这些都需要浏览器根据各种样式来计算并根据计算结果将元素放到它该出现的位置,这个过程称之为 reflow.
触发 Reflow 情况
当你增加,删除,修改 Dom 节点时会导致 Reflow 或 Repaint
当你移动 DOM 的位置,或是搞个动画的时候
当你修改 CSS 样式的时候
当你 Resize 窗口的时候,或是滚动的时候
当你修改网页的默认字体时
重绘 Repaint
重绘的定义,当各种盒子的位置,大小以及其他属性,例如颜色,字体大小等都确定下来后,浏览器于是便把这些元素都按照各自的特性绘制了一遍,于是页面的内容出现了,这个过程称为 repaint。
触发 Repaint 情况
DOM 改动
CSS 改动
讲到这里,下面来细分说一下吧!
简单介绍一下浏览器的工作原理
了解浏览器
目前使用的主流的浏览器:Internet Explorer,Firefox,Safari,Chrome 浏览器,Opera。让我们看看浏览器统计数据的占比:
让你说说浏览器的主要功能:
就是向服务器发出请求,在浏览器窗口中展示您选择的网络资源,资源一般指 HTML 文档,可以是 PDF,图片或其他的类型。资源的位置由用户使用 URI(在电脑术语中,统一资源标识符(Uniform Resource Identifier,URI)是一个用于标识某一互联网资源名称的字符串)
浏览器的结构
用户界面:包括地址栏,前进、后退按钮,书签菜单等。
浏览器引擎:在用户界面和呈现引擎之间传送指令。
呈现引擎:负责显示请求的内容。
网络:用于网络调用,比如 HTTP 请求;其接口与平台无关,并为所有平台提供底层实现。
用户界面后端:用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。
JavaScript 解释器:用于解析和执行 JavaScript 代码。
数据存储:这是持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie。新的 HTML 规范定义了“网络数据库”,这是一个完整的浏览器内数据库。
注意:Chrome 浏览器的每个标签页都分别对应一个呈现引擎实例,每个标签页都是一个独立的进程。
呈现引擎
呈现引擎的作用是“呈现”,用于在浏览器的屏幕上显示请求的内容。
一般情况下,呈现引擎可显示 HTML 和 xml 文档与图片,通过插件或浏览器扩展程序,可以显示其他类型的内容。浏览器(Firefox,Chrome 浏览器和 Safari)是基于两种呈现引擎构建的。
Firefox 使用的是 Gecko,而 Safari 和 Chrome 浏览器使用的是 WebKit(WebKit 是一种开放源代码呈现引擎)。
主流程
呈现引擎一开始会从网络层获取请求文档的内容,其大小一般限制在 8000 个块以内。
呈现引擎将开始解析 HTML 文档,并将各标记逐个转化成“内容树”上的 DOM 节点。同时也会解析外部 CSS 文件以及样式元素中的样式数据。呈现树构建完后,会进入“布局”处理阶段,也就是为每个节点分配一个应出现在屏幕上的确切坐标。
解析
解析是呈现引擎中重要的环境,什么是解析呢?
解析文档是指将文档转化成为有意义的结构,可以让代码理解和使用的结构。解析得到的结构通常是代表了文档结构的节点树,它称为解析树或者语法树。
语法
解析是以文档所遵循的语法规则为基础的。解析的过程分为两个子过程:词法分析和语法分析。
什么是词法分析呢?
词法分析是将输入内容分割成大量标记的过程,标记(语言中的词汇),构成内容的单位。相等于语言中的单词。
什么是语法分析呢?
语法分析是应用语言的语法规则的过程。
so,解析器一般解析工作分两个组件处理,为词法分析器(负责将输入内容分解成一个个有效标记),解析器负责根据语言的语法规则来分析文档的结构,来构建解析树。
从源文档到解析树:Document->Lexical Analysis->Syntax Analysis->Parse Tree
解析是一个迭代的过程。
是这样的,解析器会向词法分析器请求一个新标记,并尝试将其与某条语法规则进行匹配。如果匹配规则,解析器就会将对应与该标记的节点添加到解析树中,然后继续下一个。
但是如果没有匹配的规则,解析器会将标记存储到内部,继续请求标记,直到可与之匹配的规则,但是如果没有直到的话,就会引发异常(文档无效,包含语法错误等)。
翻译
解析通常是在翻译的过程中,而翻译是将输入的文档转换为另一种形式,如编译器将源代码编译成机器代码,流程是将源代码解析成解析树,将解析树翻译成机器代码文档。
编译流程:Source Code -> Parsing->Parse Tree -> Translation -> Machine Code
解析器类型
两种基本的解析器类型:自上而下解析器,自下而上解析器
自上而下就是: 解析器从语法的高层结构出发,尝试从中找到匹配的结构。
自下而上就是: 解析器从低层规则出发,将输入内容逐步转化为语法规则,直至满足高层规则。
你知道一种工具叫解析器生成器吗,它能够帮助你生成解析器,你只要向它提供你所使用的语言的语法,即词汇和语法规则,然后就会生成相应的解析器。
你晕了吗?可以点击这里查看:浏览器的工作原理:新式网络浏览器幕后揭秘
https://www.html5rocks.com/zh/tutorials/internals/howbrowserswork/
渲染机制
浏览器从接收到页面开始到页面显示,这整个过程中的所有步骤,称 关键渲染路径 ,一般分为两步:页面内容加载完成和页面资源完成,分别对应于 DOMContentLoaded 和 Load
关键:网页的渲染过程如下,包含页面加载和页面渲染两个过程。
页面加载过程是,从服务器请求资源并构建 DOM 树的过程,网页渲染过程指的是通过 DOM 树渲染出视图内容。
首先 浏览器加载网页内容,使用 HTML 解释器 将网页 转变 为一系列的 token,再根据 token 构建 dom 树, 当一个可见的 dom 节点 插入到 dom 树时,浏览器会构建一个 renderObject 节点并将其插入到 render 树中。
Render 树包含节点的样式信息,可以简单理解为 dom + css 构成。Render 树将交由排版引擎处理,计算出每一个 RenderObject 节点的大小和位置等信息,然后再交给由渲染引擎完成页面的内容绘制。
DOM + CSS -> Render Tree
复习一下整个关键渲染包括:
解析 HTML,生成 DOM 树(DOM)
解析 CSS,生成 CSSOM 树
将 DOM 和 CSSOM 合并,生成渲染树(Rendere-Tree)
计算渲染树的布局 Layout
将布局渲染到屏幕上 Paint
那么要问了,为什么要了解浏览器渲染页面机制呢?
了解渲染机制,主要还是为了性能的优化:
了解浏览器如何进行加载,引用外部样式文件,JS 文件时,将它们放到合适的位置,是浏览器最快的速度让文件加载完毕;了解浏览器如何进行解析,选择最优的写法,构建 DOM 结构,组织 CSS 选择器的时候,是为了提高浏览器的解析速率;了解浏览器如何进行渲染,是可以减少“重绘”,“重新布局”的消耗。
那么上面一直说了解渲染机制,出现的几个基本概念,这里先弄明白:
DOM: Document Object Model,浏览器将 HTML 解析成树形的数据结构
CSSOM: CSS Object Model,浏览器将 CSS 解析成树形的数据结构
Render Tree: DOM 和 CSSOM 合并生成 Render Tree
Layout: 计算出 Render Tree 每个节点的具体位置
Painting: 通过显卡,将 Layout 后的节点内容分别呈现到屏幕上
当浏览器获取 HTML 文件后,会自上而下加载并在加载过程中进行解析和渲染;加载就是获取资源的过程;如果在加载过程中遇到外部的 css 文件和图片,浏览器会另外发送一个请求,去获取 css 文件和图片,这个请求是异步的,并不会影响 HTML 文件的加载;但如果遇到 JavaScript 文件,HTML 文件会挂起渲染的进程,等待 JavaScript 文件加载完毕后,再继续进行渲染。
为什么需要等待 JavaScript 呢?
因为 JavaScript 可能会修改 dom,导致后面的 HTML 资源白白加载,需要等待 JavaScript 文件加载完成后,再继续渲染,so,JavaScript 文件一般写在底部 body 标签前的原因。
说说浏览器页面渲染:
第一步:在 CSS 资源还没有请求回来之前,先生成 DOM 树;
第二步:当所有的 CSS 请求回来之后,浏览器按照 CSS 的导入顺序,依次进行渲染,最后生成 CSSOM 树;
第三步:把 DOM 树和 CSSOM 树结合在一起,生成有样式,有结构的 RENDER TREE 渲染树;
最后一步:浏览器按照渲染树,在页面中进行渲染和解析
由于渲染机制过于复杂,渲染模块在在执行过程中划分了很多阶段,通过《浏览器工作原理与实践》-渲染流程上分:构建 DOM 树,样式计算,布局阶段;渲染流程下分:分层,图层绘制,栅格化(raster)操作,合成和显示。
整个渲染流程,从 HTML 到 DOM、样式计算、布局、图层、绘制、光栅化、合成和显示。
面试一问:为什么要构建 DOM 树?
答:因为浏览器不能直接理解和使用 HTML,so,需要将 HTML 转换为浏览器能够理解的结构,即是 DOM 树(树结构一般都了解了的)。
为了了解完整的 DOM 树结构,可以打开 Chrome 的“开发者工具”,或按 F12,如图下:
接下来要让 DOM 节点拥有正确的样式,这就需要样式计算了。
样式计算的目的是为了计算出 DOM 节点中每个元素的具体样式:三步走
把 CSS 转换为浏览器能够理解的结构
转换样式表中的属性值,使其标准化
计算出 DOM 树中每个节点的具体样式(涉及到 CSS 的继承规则和层叠规则)
当渲染引擎接收到 CSS 文本时,会执行一个转换操作,将 CSS 文本转换为浏览器可以理解的结构——styleSheets。属性值标准化的过程:将所有值转换为渲染引擎容易理解的、标准化的计算值。
DOM 元素最终计算的样式如图:
布局阶段
布局:计算出 DOM 树中可见元素的几何位置,第一创建布局树(构建一棵只包含可见元素布局树),第二布局计算。
面试问题:CSS 加载会阻塞页面显示吗?
css 加载不会阻塞 DOM 树的解析
css 加载会阻塞 DOM 树的渲染
css 加载会阻塞后面 js 语句的执行
so,为了避免让用户看到长时间的白屏时间,应该提高 css 的加载速度。
为了防止 css 阻塞,引起页面白屏,可以提高页面加载速度
使用 cdn
对 css 进行压缩
合理利用缓存
减少 http 请求,将多个 css 文件合并
面试问题:下载 CSS 文件阻塞了,会阻塞 DOM 树的合成吗?会阻塞页面的显示吗?
答:不会阻塞 dom 树构建的,因为 HTML 转化为 dom 树的过程,发现文件请求会交给网络进程去请求对应文件,渲染进程继续解析 HTML。
会阻塞页面的显示,当计算样式的时候需要等待 css 文件的资源进行层叠样式,资源阻塞了,会进行等待,直到网络超时,network 报出错误,渲染进程继续层叠样式计算。
说了 DOM 生成、样式计算和布局三个阶段,接下来说说后面的阶段。
说说分层:渲染引擎给页面分了很多图层,这些图层按照一定顺序叠加在一起,就形成了最终的页面。完成图层树的构建后,渲染引擎会对图层树中的每个图层进行绘制,为图层绘制。然后进行栅格化(raster)操作(绘制列表只是用来记录绘制顺序和绘制指令的列表,而实际上绘制操作是由渲染引擎中的合成线程来完成的),最后合成与显示。
页面渲染机制图如下:
渲染过程图如下:
浏览器渲染过程如下:
这里重要要说(重新说一下)两个概念回流和重绘:
当 render tree 中的一部分因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。
每个页面至少需要一次回流,就是在页面第一次加载的时候。
在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程成为重绘。
当 render tree 中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如 background-color。就叫称为重绘。
本篇文章的最后,留下一些面试题:为什么减少重绘、重排能优化 Web 性能吗?如何能减少重绘、重排呢?
阅读资料
https://www.cnblogs.com/jianjie/p/13229789.html
https://zhuanlan.zhihu.com/p/26105913
浏览器工作原理与实践
https://segmentfault.com/a/1190000018811208
❤️关注+点赞+收藏+评论+转发❤️,原创不易,鼓励笔者创作更好的文章
点赞、收藏和评论
我是Jeskson
(达达前端),感谢各位人才的:点赞、收藏和评论,我们下期见!(如本文内容有地方讲解有误,欢迎指出☞谢谢,一起学习了)
我们下期见!
文章持续更新,可以微信搜一搜「 程序员哆啦 A 梦 」第一时间阅读,回复【资料】有我准备的一线大厂资料,本文 http://www.dadaqianduan.cn/#/ 已经收录
github
收录,欢迎Star
:https://github.com/webVueBlog/WebFamily
版权声明: 本文为 InfoQ 作者【魔王哪吒】的原创文章。
原文链接:【http://xie.infoq.cn/article/ffa48b693a1d2cc0e5bd00e77】。未经作者许可,禁止转载。
评论 (1 条评论)