浏览器
浏览器渲染的五个阶段
1.第一步,浏览器开始解析html,构建DOM树。当解析器到达script标签,发生四件事
1.html停止解析
2,如果是外部脚本,从外部网络获取脚本代码
3.将控制权交给js引擎,执行js代码
4。恢复html控制权
这里可以说明<script>标签是会阻止html解析的,尽量放到网页尾部加速页面渲染
-
<script src="script.js"></script>没有
defer或async,浏览器会立即加载并执行指定的脚本,“立即”指的是在渲染该script标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行。 -
<script async src="script.js"></script>有
async,加载和渲染后续文档元素的过程将和script.js的加载与执行并行进行(异步)。下载完立刻执行 -
<script defer src="myscript.js"></script>有
defer,加载后续文档元素的过程将和script.js的加载并行进行(异步),但是script.js的执行要在html所有元素解析完成之后,DOMContentLoaded事件触发之前完成。按照文档script标签顺序执行都是异步加载,但defer要在文档加载完后执行
2.第二步,解析css,构建cssom树
css样式表是阻塞html解析的。阻塞是指当cssom树建立好之后才会进行下一步的解析渲染
通过以下手段可以减轻cssom带来的影响
- 将
script脚本放在页面底部 - 尽可能快的加载
css样式表 - 将样式表按照
media type和media query区分,这样有助于我们将css资源标记成非阻塞渲染的资源。 - 非阻塞的资源还是会被浏览器下载,只是优先级较低
第三步:把DOM和CSSOM组合成渲染树(render tree)
第四步:在渲染树的基础上进行布局,计算每个节点的几何结构
布局(
layout):定位坐标和大小,是否换行,各种position,overflow,z-index属性
第五步 调用 GPU 绘制,合成图层,显示在屏幕上**
将渲染树的各个节点绘制到屏幕上,这一步被称为绘制
painting
Reflow和Repaint
Reflow(回流) :当浏览器上某个位置的布局发生了变化,浏览器会重新从根部开始计算该节点的布局,重新验证并计算Render树。
Repaint(重画) :只改变页面元素的颜色、字体等不影响布局的属性时,浏览器会重新渲染Render树,进行重画操作。
两者的区别就是页面布局是否改变,比如:display:none会触发reflow,而visibility:hidden只会触发repaint。两者虽然都会降低页面加载速率,但是都是不可避免的,我们在设计的过程中应该减少两者发生的次数。
为什么js是单线程
因为js是服务于页面的,里面就涉及到用户的交互,以及操作 DOM 树、CSS 样式树来给用户一个页面。如果是多线程就会造成两个线程之间对数据修改导致对页面显示的冲突
为什么js阻塞页面加载
由于 JavaScript 是可操纵 DOM 的,如果在修改这些元素属性同时渲染界面(即 JavaScript 线程和 UI 线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。
因此为了防止渲染出现不可预期的结果,浏览器设置 GUI 渲染线程与 JavaScript 引擎为互斥的关系。
3. css 加载会造成阻塞吗 ?
DOM 和 CSSOM 通常是并行构建的,所以 CSS 加载不会阻塞 DOM 的解析。
然而,由于 Render Tree 是依赖于 DOM Tree 和 CSSOM Tree 的,
所以他必须等待到 CSSOM Tree 构建完成,也就是 CSS 资源加载完成(或者 CSS 资源加载失败)后,才能开始渲染。
因此,CSS 加载会阻塞 Dom 的渲染。
由于 JavaScript 是可操纵 DOM 和 css 样式 的,如果在修改这些元素属性同时渲染界面(即 JavaScript 线程和 UI 线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。
因此为了防止渲染出现不可预期的结果,浏览器设置 GUI 渲染线程与 JavaScript 引擎为互斥的关系。
因此,样式表会在后面的 js 执行前先加载执行完毕,所以css 会阻塞后面 js 的执行。