在之前写过的一篇 "天龙八步" 细说浏览器输入 URL 后发生了什么一文中, 和大家分享了从在浏览器中输入网址 URL 到最终页面展示的整个过程部分读者向我反馈对于最后的浏览器渲染布局这块不是很清晰, 所以本文就浏览器渲染流程单独开篇讲解, 希望大家都能有新的收获
浏览器主要组件结构
(浏览器主要组件)
渲染引擎 webkit 和 Gecko
Firefox 使用 GeokoMozilla 自主研发的渲染引擎
Safari 和 Chrome 都使用 webkitWebkit 是一款开源渲染引擎, 它本来是为 linux 平台研发的, 后来由 Apple 移植到 Mac 及 Windows 上
本文我主要以 webkit 渲染引擎来讲解, 尽管 webkit 和 Gecko 使用的术语稍有不同, 他们的主要流程基本相同
(webkit 渲染引擎流程)
关键渲染路径
关键渲染路径是指浏览器从最初接收请求来的 htmlCSSjavascript 等资源, 然后解析构建树渲染布局绘制, 最后呈现给客户能看到的界面这整个过程
所以浏览器的渲染过程主要包括以下几步:
解析 HTML 生成 DOM 树
解析 CSS 生成 CSSOM 规则树
将 DOM 树与 CSSOM 规则树合并在一起生成渲染树
遍历渲染树开始布局, 计算每个节点的位置大小信息
将渲染树每个节点绘制到屏幕
构建 DOM 树
当浏览器接收到服务器响应来的 HTML 文档后, 会遍历文档节点, 生成 DOM 树
需要注意的是, DOM 树的生成过程中可能会被 CSS 和 JS 的加载执行阻塞渲染阻塞问题下文会讲
构建 CSSOM 规则树
浏览器解析 CSS 文件并生成 CSS 规则树, 每个 CSS 文件都被分析成一个 StyleSheet 对象, 每个对象都包含 CSS 规则 CSS 规则对象包含对应于 CSS 语法的选择器和声明对象以及其他对象
渲染阻塞
当浏览器遇到一个 script 标记时, DOM 构建将暂停, 直至脚本完成执行, 然后继续构建 DOM 每次去执行 JavaScript 脚本都会严重地阻塞 DOM 树的构建, 如果 JavaScript 脚本还操作了 CSSOM, 而正好这个 CSSOM 还没有下载和构建, 浏览器甚至会延迟脚本执行和构建 DOM, 直至完成其 CSSOM 的下载和构建
所以, script 标签的位置很重要实际使用时, 可以遵循下面两个原则:
CSS 优先: 引入顺序上, CSS 资源先于 JavaScript 资源
JS 置后: 我们通常把 JS 代码放到页面底部, 且 JavaScript 应尽量少影响 DOM 的构建
当解析 html 的时候, 会把新来的元素插入 dom 树里面, 同时去查找 css, 然后把对应的样式规则应用到元素上, 查找样式表是按照从右到左的顺序去匹配的
例如: div p {font-size: 16px}, 会先寻找所有 p 标签并判断它的父标签是否为 div 之后才会决定要不要采用这个样式进行渲染)
所以, 我们平时写 CSS 时, 尽量用 id 和 class, 千万不要过渡层叠
构建渲染树
通过 DOM 树和 CSS 规则树我们便可以构建渲染树浏览器会先从 DOM 树的根节点开始遍历每个可见节点对每个可见节点, 找到其适配的 CSS 样式规则并应用
渲染树构建完成后, 每个节点都是可见节点并且都含有其内容和对应规则的样式这也是渲染树与 DOM 树的最大区别所在渲染树是用于显示, 那些不可见的元素当然就不会在这棵树中出现了, 譬如除此之外, display 等于 none 的也不会被显示在这棵树里头, 但是 visibility 等于 hidden 的元素是会显示在这棵树里头的
渲染树布局
布局阶段会从渲染树的根节点开始遍历, 然后确定每个节点对象在页面上的确切大小与位置, 布局阶段的输出是一个盒子模型, 它会精确地捕获每个元素在屏幕内的确切位置与大小
渲染树绘制
在绘制阶段, 遍历渲染树, 调用渲染器的 paint() 方法在屏幕上显示其内容渲染树的绘制工作是由浏览器的 UI 后端组件完成的
reflow 与 repaint:
根据渲染树布局, 计算 CSS 样式, 即每个节点在页面中的大小和位置等几何信息 HTML 默认是流式布局的, CSS 和 js 会打破这种布局, 改变 DOM 的外观样式以及大小和位置这时就要提到两个重要概念: replaint 和 reflow
replaint: 屏幕的一部分重画, 不影响整体布局, 比如某个 CSS 的背景色变了, 但元素的几何尺寸和位置不变
reflow: 意味着元件的几何尺寸变了, 我们需要重新验证并计算渲染树是渲染树的一部分或全部发生了变化这就是 Reflow, 或是 Layout
所以我们应该尽量减少 reflow 和 replaint, 我想这也是为什么现在很少有用 table 布局的原因之一
display:none 会触发 reflow,visibility: hidden 属性并不算是不可见属性, 它的语义是隐藏元素, 但元素仍然占据着布局空间, 它会被渲染成一个空框, 所以 visibility:hidden 只会触发 repaint, 因为没有发生位置变化
有些情况下, 比如修改了元素的样式, 浏览器并不会立刻 reflow 或 repaint 一次, 而是会把这样的操作积攒一批, 然后做一次 reflow, 这又叫异步 reflow 或增量异步 reflow
有些情况下, 比如 resize 窗口, 改变了页面默认的字体等对于这些操作, 浏览器会马上进行 reflow
小结
本文我们就浏览器渲染流程逐步了解了一遍, 相信大家一定都有所新的收获, 如果大家对于浏览器渲染流程还有任何疑问, 欢迎反馈, 我们共同交流, 共同学习, 共同进步
参考文献:
- http://taligarsiel.com/Projects/howbrowserswork1.htm
- https://segmentfault.com/a/1190000012960187
- https://sylvanassun.github.io/2017/10/03/2017-10-03-BrowserCriticalRenderingPath/
- https://www.zybuluo.com/lizlalala/note/435042
来源: http://www.xuecaijie.com/htmlcss/172.html