构建过程中可能会产生的阻塞
html
的代码,是从上到下一行行执行的,也就是说如果js
代码写在head
头里,且没有用加在window.onload
方法里,那么他是无法读取到body
里的标签的。之所以加在window.onload
里可以执行,是因为,window.onload
里的函数会在dom
树加载之后执行。- 在遇到
link
标签后,会在link
加载(从服务器下载)完毕后,再执行后续代码。但与此同时,如果还有外部文件,则是同时加载(不阻塞后续外部文件link
、script
加载)。但是外部文件内的代码不会执行,只会在代码解析到它的时候执行。 script
标签会阻塞html
解析,因为js
可能会改变dom
和css
,因此浏览器会先解析script,避免浪费时间。 要想避免阻塞的话,可使用defer
和async
。- 对于动态创建的
link
标签不会阻塞其后动态创建的script
的加载与执行,不管script
标签是否具有async
属性。<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>JS Bin</title> <script> var start = +new Date </script> </head> <body> test <script> var link = document.createElement('link') link.href = 'http://udacity-crp.herokuapp.com/style.css?rtt=2' link.rel = 'stylesheet' document.head.appendChild(link) var script = document.createElement('script') script.src = 'http://udacity-crp.herokuapp.com/time.js?rtt=1&a' document.head.appendChild(script) </script> <div id="result"></div> <script> var end = +new Date document.getElementById('result').innerHTML = end - start </script> </body> </html>
js
添加async
属性之后,script
加载的外部文件成为了异步加载,这时相当于它于原本的html
解析过程同步进行。所以他不会被任何加载过程阻塞,只会在自己加载完成之后执行。但是,异步执行的影响就是,它如要读取dom
节点,很可能会失败,因为它的加载和html
解析过程没有了先后顺序。另外,如果它要输出动态的dom
节点,就无法保证节点的位置,因为它添加的节点,是在html
已解析的节点下顺序添加的。<script src="http://localhost:8080/test.js" async></script>
- 如下代码,加上
js
添加defer
属性之后,script
加载的外部文件成为了异步加载,执行是同步的。脚本加载不阻塞页面的解析,脚本在获取完后并不立即执行,而是等到DOM
树加载完毕执行。defer
后会报错Uncaught ReferenceError: $ is not defined
。DOM
树渲染结束前body
里的script
已经执行了<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>JS Bin</title> <script defer src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> </head> <body> <div id="d1">我是内容</div> <script > console.log($('#d1').html()) </script> </body> </html>
什么情况会引起重排/回流(reflow)
- 添加或者删除可见的
DOM
元素; - 元素位置改变——
display、float、position、overflow
等等; - 元素尺寸改变——边距、填充、边框、宽度和高度
- 内容改变——比如文本改变或者图片大小改变而引起的计算值宽度和高度改变;
- 页面渲染初始化;
- 浏览器窗口尺寸改变——resize事件发生时;
如何减少和避免重排
Reflow
的成本比Repaint
的成本高得多的多。一个节点的 Reflow 很有可能导致子节点,甚至父节点以及兄弟节点的Reflow
。- 直接改变
className
,如果动态改变样式,则使用cssText
(考虑没有优化的浏览器) - 让要操作的元素进行”离线处理”,处理完后一起更新;
- 使用
DocumentFragment
进行缓存操作,引发一次回流和重绘; - 使用
display:none
技术,只引发两次回流和重绘; - 使用
cloneNode(true or false)
和replaceChild
技术,引发一次回流和重绘;
- 不要经常访问会引起浏览器
flush
队列的属性,如果你确实要访问,利用缓存; - 让元素脱离动画流,减少回流的
Render Tree
的规模;
DOM是什么?
DOM(Document Object Model——文档对象模型)是用来呈现以及与任意 HTML 或 XML文档交互的API。DOM 是载入到浏览器中的文档模型,以节点树的形式来表现文档,每个节点代表文档的构成部分(例如:页面元素、字符串或注释等等)。
DOM的作用
- DOM 将HTML文档呈现为带有元素、属性和文本的树结构(节点树)。
- 它允许运行在浏览器中的代码访问文件中的节点并与之交互。节点可以被创建,移动或修改。事件监听器可以被添加到节点上并在给定事件发生时触发。
什么是DOM渲染?
DOM渲染指的是对于浏览器中展现给用户的DOM文档的生成的过程。
DOM树的构建是文档加载完成开始的?
构建DOM树是一个渐进过程,为达到更好用户体验,渲染引擎会尽快将内容显示在屏幕上。它不必等到整个HTML文档解析完毕之后才开始构建render数和布局。
Render树是DOM树和CSSOM树构建完毕才开始构建的吗?
这三个过程在实际进行的时候不是完全独立,而是会有交叉。会造成一边加载,一遍解析,一遍渲染的工作现象。