浏览器解析html步骤
- 浏览器通过DNS服务器得到域名的IP地址,向这个IP地址请求得到HTML文本
- 浏览器渲染进程解析HTML文本,构建DOM树
- 解析HTML的同时,如果遇到内联样式或者样式文件,则下载并构建样式规则,如果遇到JavaScript脚本,则会下载执行脚本
- DOM树和CSSOM构建完成之后,渲染进程将两者合并成渲染树(render tree)
- 渲染进程开始对渲染树进行布局,生成布局树(layout tree)
- 渲染树对布局树进行绘制,生成绘制记录
解析方式
html解析
逐行解析 遇到引入资源马上去下载引入(不阻塞) js马上执行(阻塞DOM解析)
css解析
从右往左解析选择器
易错问题
CSS不会阻塞DOM的解析,但会阻塞DOM的渲染
因为CSS需要和DOM解析完成之后的DOM树结合成渲染树。阻塞渲染是因为CSS树和DOM树合成渲染树之后,才要进行渲染。
CSS会阻塞JS执行,但不会阻塞JS文件的下载
JavaScript线程与UI线程是互斥的,如果不互斥,js中涉及到对样式的增删改,css又同事对该元素样式进行绘制,会发生不可预料的错误。
JS需要等待CSS的下载
如果JS脚本的内容是获取元素的样式,那它就必然依赖CSS。因为浏览器无法感知JS内部到底想干什么,为避免样式获取,就只好等前面所有的样式下载完毕再执行JS。但JS文件与CSS文件下载是并行的,CSS文件会在后面的JS文件执行前先加载执行完毕,所以CSS会阻塞后面JS的执行
JS会阻塞页面吗?
JS会阻塞DOM的解析,因此也就会阻塞页面的加载
由于 JavaScript 是可操纵 DOM 的,如果在修改这些元素属性同时渲染界面(即 JavaScript 线程和 UI 线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。
defer和async的区别?
两者都是异步去加载外部JS文件,不会阻塞DOM解析
- Async是在外部JS加载完成后,浏览器空闲时,Load事件触发前执行,标记为async的脚本并不保证按照指定他们的先后顺序执行,该属性对于内联脚本无作用 (即没有**「src」**属性的脚本)。
- defer是在JS加载完成后,整个文档解析完成后,触发 DOMContentLoaded 事件前执行,如果缺少 src 属性(即内嵌脚本),该属性不应被使用,因为这种情况下它不起作用
重排和重绘
- 重排:当某个元素的大小、位置发生了变化时,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树;
- 重绘:当某个元素的字体颜色等不影响布局的属性变化时,不需要重新构建渲染树,只需直接重新绘制页面即可;
下面HelloWorld如何输出
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>测试</title>
</head>
<body>
<h1>Hello</h1>
<script type="text/javascript">
let i = 1000000000
while(i>0){
i--
}
</script>
<h1>world</h1>
</body>
</html>
一开始渲染进程的HTML 解析器开始解析DOM,当解析到内联script 脚本标签时,HTML 解析器会暂停解析DOM,此时JavaScript 引擎介入,并执行内联script 标签中的这段脚本,脚本执行完成之后,HTML 解析器恢复解析过程,继续解析DOM,然后进行后续的渲染,最终将页面上同时渲染出 Hello World
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>测试</title>
</head>
<body>
<h1>Hello</h1>
<script type="text/javascript" src="index.js"></script>
<h1>world</h1>
</body>
</html>
先渲染出Hello ,然后过一会儿(这个期间在执行js),再渲染出World