网页的渲染机制
呈现引擎的基本流程如下:
- 呈现引擎将开始解析 HTML 文档,并将各标记逐个转化成“内容树”上的 DOM节点。同时也会解析外部 CSS 文件以及样式元素中的样式数据。HTML 中这些带有视觉指令的样式信息将用于创建另一个树结构:呈现树。
呈现树包含多个带有视觉属性(如颜色和尺寸)的矩形。这些矩形的排列顺序就是它们将在屏幕上显示的顺序。 - 呈现树构建完毕之后,进入“布局”处理阶段,也就是为每个节点分配一个应出现在屏幕上的确切坐标。下一个阶段是绘制, 呈现引擎会遍历呈现树,由用户界面后端层将每个节点绘制出来。
注:需要着重指出的是,这是一个渐进的过程。为达到更好的用户体验,呈现引擎会力求尽快将内容显示在屏幕上。它不必等到整个 HTML 文档解析完毕之后,就会开始构建呈现树和设置布局。在不断接收和处理来自网络的其余内容的同时,呈现引擎会将部分内容解析并显示出来。
浏览器的渲染顺序
需要注意一下几点:
- 默认的,浏览器会按照顺序从上到下的解析(parse)HTML页面。当遇到需要加载的资源时(如stylesheets, images or scripts),浏览器会并行的请求这些资源。
- stylesheets和images不会阻止浏览器对HTML页面的进一步解析工作。也就是说在css文件加载和执行的过程中,浏览器对的其他工作正常进行。
-
<script>
标签(先不讨论其具有defer或async属性),它在下载和执行(download and execute)的过程中,会阻止浏览器对HTML页面的解析。作为性能优化,browsers may "look-ahead" at other resources in the page and may fetch them in parallel. For example, multiple script files may be fetched in parallel, even though their execution must be serially.即此时仍可以下载后面的其他资源。
CSS和JS在网页中的放置顺序
一般而言,因为HTML是一种相对松散的文档类型,同时浏览器的容错性又非常高,所以CSS和JS可以放置在页面上的任何位置,甚至可以放到的外部。但是在实际使用中,我们习惯上把CSS文件放到head标签内部,这是由浏览器的渲染机制决定的,因为大部分浏览器都是要等HTML和CSS同时加载一部分后才开始进行网页布局并最终绘制到浏览器窗口;JS则更加的苛刻,因为浏览器的解析器遇到JS脚本后会立刻开始执行JS脚本,这样就会阻塞HTML、CSS的解析,浏览器出现白屏,只有当JS脚本执行完毕,才会继续后面文档的渲染,所以我们一般把JS放到HTML文档的尾部,这样就不会出现白屏现象,用户体验比较好。
白屏和FOUC(flash of unstyled content)
Webkit内核的浏览器的渲染过程中页面的绘制要等HTML和CSS都加载完毕才开始,所以在这个加载过程中浏览器会出现白屏现象。
- 如果把样式放在文档底部,对于IE浏览器,在某些场景下(新窗口打开,刷新等)页面会出现白屏,而不是内容逐步展现
- 如果使用 @import 标签,即使 CSS 放入 link, 并且放在头部,也可能出现白屏
- 对于图片和CSS, 在加载时会并发加载(如一个域名下同时加载两个文件). 但在加载 JavaScript 时,会禁用并发,并且阻止其他内容的下载. 所以把 JavaScript 放入页面顶部也会导致白屏现象.
Gecko内核的浏览器则是在加载过程中优先渲染HTML部分,在CSS加载的时候再次进行页面的二次渲染,这样渲染到页面上的无样式内容时就会不断的进行页面二次渲染,无样式内容就会闪烁,这就是FOUC现象。
- 如果把样式放在底部,对于IE浏览器,在某些场景下(点击链接,输入URL,使用书签进入等),会出现 FOUC 现象(逐步加载无样式的内容,等CSS加载后页面突然展现样式).对于 Firefox 会一直表现出 FOUC .
async和defer
async表示应该立即下载脚本,但不妨碍页面中的其他操作。它表示当前脚本不必在等待其他脚本,也不会阻塞文档呈现。但异步的脚本不保证按他们在页面中出现的顺序执行。
defer相当于告诉浏览器立即下载,但要延迟到文档完全被解析和显示之后再执行,不过会先于DOMContentLoaded时间触发前执行。HTML5规范要求延迟脚本按照他们出现的先后顺序执行。
由于异步脚本并不保证执行顺序,因此确保他们之间互不依赖非常重要。指定async属性的目的是不让页面等待脚本下载和执行,从而异步的加载页面其他内容。为此,建议异步脚本不要在加载期间修改DOM。