人法地,地法天,天法道,道法天然。javascript
若是想看更深刻的原理,能够看:java
别人翻译的外国友人的渲染原理node
关键渲染路径(Critical Rendering Path)
是指与当前用户操做有关的内容。例如用户刚刚打开一个页面,首屏的显示就是当前用户操做相关的内容,具体就是浏览器收到HTML、CSS 和 JavaScript
等资源并对其进行处理从而渲染出 Web
页面。 以下图所示渲染流程: web
- 浏览器首先经过
HTTP
协议或者HTTPS
协议,向服务器请求页面,固然这个其中也可能有缓存什么的;- 把请求回来的
HTML
代码通过解析,构建成DOM
树;- 计算
DOM 树
上的CSS
属性,生成CSSOM 树(CSS Object Model)
;- 将
DOM 树
和CSSOM 树
合并成一个渲染树(rendering tree)
;- 渲染树的每一个元素包含的内容都是
计算
过的,它被称之为布局 layout
。浏览器使用一种流式处理的方法,只须要一次pass 绘制
操做就能够布局全部的元素;- 将渲染树的各个节点绘制到屏幕上,这一步被称为
绘制 painting
;- 按照合理的顺序合并图层而后显示到屏幕上
Composite(渲染层合并)
在咱们在浏览器中输入完网址的时候,浏览器其实会先作如下几小步:浏览器
DNS 查询
(就是把当前域名解析成为 ip 地址)TCP 链接
HTTP 请求响应
- 服务器返回数据
在构建 DOM 树
的时候又能够分为几小步:缓存
- 字符流经过状态机解析成为
词 token
- 词
token => prase => DOM 树
构建 DOM
的过程是:从父到子,从先到后,一个一个节点构造,DOM 树
结构和 HTML 标签一一对应。服务器
在计算 css 规则
的时候,咱们会在已经构建好的元素上,去检查它匹配到了哪些规则,再根据规则的优先级,作覆盖和调整。而且 CSSOM
主要是DOM 结构
上的盒的描述,他基本上是依附于 DOM 树
的。 CSS 计算
是把 CSS 规则
应用到 DOM 树
上,为 DOM 结构
添加显示相关属性过程。 CSSOM
是有 rule
部分和 view
部分的,rule 部分
是在 dom
开始以前就构件完成的,而 view
部分是跟着 dom
同步构建的。
经过 DOM 树
和 CSS 规则树
,浏览器就能够经过它两构建渲染树
了。 渲染树
和 DOM 元素
相对应的,但并不是一一对应。非可视化的 DOM 元素
不会插入呈现树中,例如“head”元素
。若是元素的 display
属性值为“none”
,那么也不会显示在呈现树中(可是 visibility
属性值为“hidden”
的元素仍会显示)。
呈现器在建立完成并添加到呈现树时,并不包含位置和大小信息。计算这些值的过程称为布局
或重排
。 布局阶段会从渲染树
更新节点开始遍历,因为渲染树的每一个节点都是一个 Render Object
对象,包含宽高,位置,背景色等样式信息。浏览器中渲染这个过程,就是把每个元素对应的盒变成位图
,再把位图合成一个大的位图
。 布局又分为全局布局和增量布局,详情请看
在绘制阶段,系统会遍历呈现树
,并调用呈现器的“paint”方法
,将呈现器
的内容显示在屏幕上。绘制工做是使用用户界面基础组件完成的。 绘制又分为全局绘制
和增量绘制
,而且绘制的属性也会有先后之分,详情请看
渲染过程把元素变成位图
,合成把一部分位图变成合成层
,最终的绘制过程把合成层
显示到屏幕上。 对于 transform/opacity
这两种变换,浏览器不会用 repaint/reflow
处理,而是在已经渲染的元素基础上进行附加工做。 他的渲染流程为下图所示:
谈论资源的阻塞时,咱们要清楚,现代浏览器老是并行加载资源。例如,当 HTML 解析器(HTML Parser)
被脚本阻塞时,解析器虽然会中止构建 DOM,但仍会识别该脚本后面的资源,并进行预加载。 同时,因为下面两点:
- 默认状况下,
CSS 被视为阻塞渲染的资源
,这意味着浏览器将不会渲染任何已处理的内容,直至CSSOM 构建完毕
。JavaScrip
t 不只能够读取和修改 DOM 属性
,还能够读取和修改 CSSOM
属性,所以CSS 解析
与script 的执行
互斥。- 存在
阻塞的 CSS 资源
时,浏览器会延迟 JavaScript 的执行和 DOM 构建
。
正是因为以上这些缘由,script 标签
的位置很重要咱们在实际开发中应该尽可能坚持如下两个原则: 在引入顺序上,CSS 资源先于 JavaScript 资源。 JavaScript 应尽可能少的去影响 DOM 的构建。 想理清楚 CSS、JavaScript、DOM
之间的相互阻塞关系
咱们熟知的javascript
标签上defer
和async
属性,还有可能不太熟知的link
标签上的preload
属性。
在介绍 async 和 defer 以前咱们要先看了解两个概念,load
和DOMContentLoaded
的执行时机
load Load 事件触发表明页面中的 DOM,CSS,JS,图片已经所有加载完毕。 DOMContentLoaded DOMContentLoaded 事件触发表明初始的 HTML 被彻底加载和解析,不须要等待 CSS,JS,图片加载。
async 和 defer 他们对于内联脚本无做用(即没有 src 属性的脚本) async 该布尔属性指示浏览器是否在容许的状况下异步执行该脚本。async 与 defer 的区别在于,若是已经加载好,就会开始执行——不管此刻是 HTML 解析阶段仍是 DOMContentLoaded 触发以后。须要注意的是,这种方式加载的 JavaScript 依然会阻塞 load 事件。换句话说,async-script 可能在 DOMContentLoaded 触发以前或以后
执行,但必定在 load 触发以前
执行。而且多个 async-script 的执行顺序是不肯定
的。
defer defer 属性表示延迟执行引入的 JavaScript,即这段 JavaScript 加载时 HTML 并未中止
解析,这两个过程是并行
的。整个 document 解析完毕且 defer-script 也加载完成以后(这两件事情的顺序无关),会执行全部由 defer-script 加载的 JavaScript 代码,而后触发
DOMContentLoaded 事件。
defer 与相比普通 script,有两点区别:载入 JavaScript 文件时不阻塞
HTML 的解析,执行阶段被放到 HTML 标签解析完成
以后。
preload
<link>
元素的 rel 属性
的属性值preload
可以让你在你的HTML页面
中 <head>元素
内部书写一些声明式的资源获取请求,能够指明哪些资源是在页面加载完成后即刻须要的
。对于这种即刻须要的资源,你可能但愿在页面加载的生命周期的早期阶段就开始获取,在浏览器的主渲染机制介入前就进行预加载
。这一机制使得资源能够更早的获得加载并可用,且更不易阻塞页面的初步渲染,进而提高性能。 预加载能够必定程度上下降首屏的加载时间,由于能够将一些不影响首屏但重要的文件延后加载,惟一缺点就是兼容性很差.
prerender 能够经过预渲染将下载的文件预先在后台渲染
,可使用如下代码开启预渲染
预渲染虽然能够提升页面的加载速度
,可是要确保该页面百分百会被用户在以后打开,不然就白白浪费资源去渲染
这个里面基本上了解了浏览器的渲染过程,可是有不少细节没有套路好比说咱们都知道浏览器是单线程的,ui 线程
和 javascript 线程
是怎么协调的,还有一个比较重要的是重绘和回流(重排)
。