金沙棋牌官方平台

当前位置:金沙棋牌 > 金沙棋牌官方平台 > 网页的渲染机制,JS相关概念

网页的渲染机制,JS相关概念

来源:http://www.logblo.com 作者:金沙棋牌 时间:2019-11-22 11:06

优化reflow、repaint触发次数

  • 避免逐个修改节点样式,尽量一次性修改
  • 使用DocumentFragment将需要多次修改的DOM元素缓存,最后一次性append到真实DOM中渲染
  • 可以将需要多次修改的DOM元素设置display: none,操作完再显示。(因为隐藏元素不在render树内,因此修改隐藏元素不会触发回流重绘)
  • 避免多次读取某些属性(见上)
  • 将复杂的节点元素脱离文档流,降低回流成本

绘制(PAINTING)

最后是绘制(paint)阶段或重绘(repaint)阶段,浏览器UI组件将遍历渲染树并调用渲染对象的绘制(paint)方法,将内容展现在屏幕上,也有可能在之后对DOM进行修改,需要重新绘制渲染对象,也就是重绘,绘制和重绘的关系可以参考布局和回流的关系。

常见触发场景:

![](https://upload-images.jianshu.io/upload_images/5979792-da9339f37371bfe8.jpg)

image



为了更友好的用户体验,浏览器会尽可能快的展现内容,而不会等到文档所有内容到达才开始解析和构建/布局渲染树,而是每次处理一部分,并展现在屏幕上,这也是为什么我们经常可以看到页面加载的时候内容是从上到下一点一点展现的。

2.构建CSSOM树

上述也提到了CSSOM的构建过程,也是树的结构,在最终计算各个节点的样式时,浏览器都会先从该节点的普遍属性(比如body里设置的全局样式)开始,再去应用该节点的具体属性。还有要注意的是,每个浏览器都有自己默认的样式表,因此很多时候这棵CSSOM树只是对这张默认样式表的部分替换。

渲染树与DOM树

每一个渲染对象都对应着DOM节点,但是非视觉(隐藏,不占位)DOM元素不会插入渲染树,如<head>元素或声明display: none;的元素,渲染对象与DOM节点不是简单的一对一的关系,一个DOM可以对应一个渲染对象,但一个DOM元素也可能对应多个渲染对象,因为有很多元素不止包含一个CSS盒子,如当文本被折行时,会产生多个行盒,这些行会生成多个渲染对象;又如行内元素同时包含块元素和行内元素,则会创建一个匿名块级盒包含内部行内元素,此时一个DOM对应多个矩形对象(渲染对象)。渲染树及其对应DOM树如图:

图片 1

渲染树与DOM树

  • 图中渲染树viewport即视口,是文档的初始包含块,scroll代表滚动区域
  • 渲染树并不会包含显式或隐式地display:none;的标签元素。

6:如何异步加载脚本

<script src="script.js"></script>

没有 defer 或 async,浏览器会立即加载并执行指定的脚本,“立即”指的是在渲染该 script 标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行。

<script async src="script.js"></script>

有 async,加载和渲染后续文档元素的过程将和 script.js 的加载与执行并行进行(异步)。

<script defer src="script.js"></script>

有 defer,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但 script.js 的执行要在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成。

defer:脚本延迟到文档解析和显示后执行,有顺序。
async:不保证顺序。

  1. 构建DOM树(DOM tree):从上到下解析HTML文档生成DOM节点树(DOM tree),也叫内容树(content tree);
  2. 构建CSSOM(CSS Object Model)树:加载解析样式生成CSSOM树;
  3. 执行JavaScript:加载并执行JavaScript代码(包括内联代码或外联JavaScript文件);
  4. 构建渲染树(render tree):根据DOM树和CSSOM树,生成渲染树(render tree);
  5. 布局(layout):根据渲染树将节点树的每一个节点布局在屏幕上的正确位置;
  6. 绘制(painting):遍历渲染树绘制所有节点,为每一个节点适用对应的样式,这一过程是通过UI后端模块完成;

Tips

  • 在上述渲染过程中,前3点可能要多次执行,比如js脚本去操作dom、更改css样式时,浏览器又要重新构建DOM、CSSOM树,重新render,重新layout、paint;
  • Layout在Paint之前,因此每次Layout重新布局(reflow 回流)后都要重新出发Paint渲染,这时又要去消耗GPU;
  • Paint不一定会触发Layout,比如改个颜色改个背景;(repaint 重绘)
  • 图片下载完也会重新出发Layout和Paint;

图片 2

构建CSSOM树

CSSOM树,与DOM树结构相似,只是另外为每一个节点关联了样式信息。

theme.css样式内容如下:

 html, body {
    width: 100%;
    height: 100%;
    background-color: #fcfcfc;
}
.title {
font-size: 20px;
}
.footer {
font-size: 12px;
color: #aaa;
}

构建CSSOM树如图:

图片 3

构建CSSOM树.png

5:repaint和 reflow

开发一个页面时,不可避免的需要进行repaint和reflow。也就只有古来的静态页面才会不存在repaint和reflow。repaint主要是针对某一个DOM元素进行的重绘,reflow则是回流,针对整个页面的重排。字面意思来说:repaint就是重绘,reflow就是回流。repaint和reflow的目的是:展示一个新的页面样貌。

体现:
repaint是某个DOM元素进行重绘;reflow是整个页面进行重排,也就是页面所有DOM元素渲染。

如何触发:
style变动造成repaint和reflow。

不涉及任何DOM元素的排版问题的变动为repaint,例如元素的color/text-align/text-decoration等等属性的变动。

除上面所提到的DOM元素style的修改基本为reflow。例如元素的任何涉及长、宽、行高、边框、display等style的修改。

构建CSSOM树

CSSOM树,与DOM树结构相似,只是另外为每一个节点关联了样式信息。
theme.css样式内容如下:

html, body {
    width: 100%;
    height: 100%;
    background-color: #fcfcfc;
    }
    .title {
    font-size: 20px;
    }
    .footer {
    font-size: 12px;
    color: #aaa;
    }

构建CSSOM树如图:

图片 4

Paste_Image.png

引起repaint重绘

  1. reflow回流必定引起repaint重绘,重绘可以单独触发
  2. 背景色、颜色、字体改变(注意:字体大小发生变化时,会触发回流)

3.async和defer的作用是什么?有什么区别

<script src="script.js"></script>

没有 defer 或 async,浏览器会立即加载并执行指定的脚本,“立即”指的是在渲染该 script 标签之下的文档元素之前,也就是说不等待后续载入的文档元素,读到就加载并执行。

<script async src="script.js"></script>

有 async,加载和渲染后续文档元素的过程将和 script.js 的加载与执行并行进行(异步)。

<script defer src="script.js"></script>

有 defer,加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但 script.js 的执行要在所有元素解析完成之后,DOMContentLoaded 事件触发之前完成。

在有 async 的情况下,JavaScript 脚本一旦下载好了就会执行,所以很有可能不是按照原本的顺序来执行的。如果 JavaScript 脚本前后有依赖性,使用 async 就很有可能出现错误。


4:白屏和 FOUC

1:白屏
IE 会出现白屏现象,这是因为,其等待页面组件包括样式表全部加载完成后才呈现整个页面。若样式表放在页面底部,将会出现白屏。——样式表在页面中位置并不影响页面中组件的下载时间,但是会影响页面的呈现。
在IE中,将样式表放在文档底部公导致白屏问题的情形有以下几种:
一、在新窗口中打开时
二、重新加载时
三、作为主页打开时
2:什么是FOUC(文档样式短暂失效)?
如果使用import方法对CSS进行导入,会导致某些页面在Windows 下的Internet Explorer出现一些奇怪的现象:以无样式显示页面内容的瞬间闪烁,这种现象称之为文档样式短暂失效(Flash of Unstyled Content),简称为FOUC。
原因大致为:
1,使用import方法导入样式表。
2,将样式表放在页面底部
3,有几个样式表,放在html结构的不同位置。
其实原理很清楚:当样式表晚于结构性html加载,当加载到此样式表时,页面将停止之前的渲染。此样式表被下载和解析后,将重新渲染页面,也就出现了短暂的花屏现象。
为了很好的避免白屏和FOUC问题,请遵循以下规则:
使用LINK标签将样式表放在文档的HEAD中

构建渲染树(render tree)

渲染树,代表一个文档的视觉展示,浏览器通过它将文档内容绘制在浏览器窗口,展示给用户,它由按顺序展示在屏幕上的一系列矩形对象组成,这些矩形对象都带有字体,颜色和尺寸,位置等视觉样式属性。对于这些矩对象,FireFox称之为框架(frame),Webkit浏览器称之为渲染对象(render object, renderer),后文统称为渲染对象。
渲染树及其对应DOM树如图:

图片 5

Paste_Image.png

引起reflow回流

现代浏览器会对回流做优化,它会等到足够数量的变化发生,再做一次批处理回流。

  1. 页面第一次渲染(初始化)
  2. DOM树变化(如:增删节点)
  3. Render树变化(如:padding改变)
  4. 浏览器窗口resize
  5. 获取元素的某些属性:
    浏览器为了获得正确的值也会提前触发回流,这样就使得浏览器的优化失效了,这些属性包括offsetLeft、offsetTop、offsetWidth、offsetHeight、 scrollTop/Left/Width/Height、clientTop/Left/Width/Height、调用了getComputedStyle()或者IE的currentStyle

4.简述网页的渲染机制

  1. 解析html构建DOM树
  2. 解析CSS构建CSSOM树
  3. 把DOM和CSSOM组合成渲染树(Render Tree)
  4. 在渲染树的基础上进行布局,计算每个节点的几何结构(Layout Tree)
  5. 把每个节点绘制到屏幕上(Painting)

图片 6

浏览器渲染.png

图片 7

关键渲染路径.png

为了更友好的用户体验,浏览器会尽可能快的展现内容,而不会等到文档所有内容到达才开始解析和构建/布局渲染树,而是每次处理一部分,并展现在屏幕上,这也是为什么我们经常可以看到页面加载的时候内容是从上到下一点一点展现的。

2:浏览器的渲染机制

1:DOM:Document Object Model,浏览器将HTML解析成树形的数据结构,简称DOM。
2:CSSOM:CSS Object Model,浏览器将CSS解析成树形的数据结构,简称CSSOM。
3:Render Tree: DOM和CSSOM合并后生成Render Tree
4:Layout: 计算出Render Tree每个节点的具体位置。
5:Painting:通过显卡,将Layout后的节点内容分别呈现到屏幕上。

构建DOM树

DOM树,即文档内所有节点构成的一个树形结构。
假设浏览器获取返回的如下HTML文档:

 <!doctype html>
    <html>
    <head>
      <link rel="stylesheet" href="./theme.css"></link>
      <script src="./config.js"></script>
      <title>关键渲染路径</title>
    </head>
    <body>
      <h1 class="title">关键渲染路径</h1>
      <p>关键渲染路径介绍</p>
      <footer>@copyright2017</footer>
    </body>
    </html>

首先浏览器从上到下依次解析文档构建DOM树,如下:

图片 8

Paste_Image.png

为什么一再强调将css放在头部,将js文件放在尾部

构建渲染树(RENDER TREE)

DOM树和CSSOM树都构建完了,接着浏览器会构建渲染树:

渲染树,代表一个文档的视觉展示,浏览器通过它将文档内容绘制在浏览器窗口,展示给用户,它由按顺序展示在屏幕上的一系列矩形对象组成,这些矩形对象都带有字体,颜色和尺寸,位置等视觉样式属性。对于这些矩对象,FireFox称之为框架(frame),Webkit浏览器称之为渲染对象(render object, renderer),后文统称为渲染对象。

这里把渲染树节点称为矩形对象,是因为,每一个渲染对象都代表着其对应DOM节点的CSS盒子,该盒子包含了尺寸,位置等几何信息,同时它指向一个样式对象包含其他视觉样式信息。

1:触发repaint:

  • color的修改,如color=#ddd;
  • text-align的修改,如text-align=center;
  • a:hover也会造成重绘。
  • :hover引起的颜色等不导致页面回流的style变动。
  • 等等太多,一时间写出来也太难想了。

解析文档(parser HTML)

解析顺序:浏览器按从上到下的顺序扫描解析文档;

解析样式和脚本

脚本:或许是由于通常会在JavaScript脚本中改变文档DOM结构,于是浏览器以同步方式解析,加载和执行脚本,浏览器在解析文档时,当解析到<script>标签时,会解析其中的脚本(对于外链的JavaScript文件,需要先加载该文件内容,再进行解析),然后立即执行,这整个过程都会阻塞文档解析,直到脚本执行完才会继续解析文档。就是说由于脚本是同步加载和执行的,它会阻塞文档解析,这也解释了为什么现在通常建议将<script>标签放在标签前面,而不是放在<head>标签里。现在HTML5提供defer和async两个属性支持延迟和异步加载JavaScript文件,如:<script defer src="script.js">

改进:针对上文说的脚本阻塞文档解析,主流浏览器如Chrome和FireFox等都有一些优化,比如在执行脚本时,开启另一个进程解析剩余的文档以找出并加载其他的待下载外部资源(不改变主进程的DOM树,仅优化加载外部资源)。

样式:不同于脚本,浏览器对样式的处理并不会阻塞文档解析,大概是因为样式表并不会改变DOM结构。

样式表与脚本:你可能想问样式是否会阻塞脚本文件的加载执行呢?正常情况是不会的,但是存在一个问题是通常我们会在脚本中请求样式信息,但是在文档解析时,如果样式尚未加载或解析,将会得到错误信息,对于这一问题,FireFox浏览器和Webkit浏览器处理策略不同:
当存在有样式文件未被加载和解析时,FireFox浏览器会阻塞所有脚本;
而Webkit浏览器只会阻塞操作了改文件内声明的样式属性的脚本。

首屏优化Tips

说了这么多,其实可以总结几点浏览器首屏渲染优化的方向

  • 减少资源请求数量(内联亦或是延迟动态加载)
  • 使CSS样式表尽早加载,减少@import的使用,因为需要解析完样式表中所有import的资源才会算CSS资源下载完
  • 异步js:阻塞解析器的 JavaScript 会强制浏览器等待 CSSOM 并暂停 DOM 的构建,导致首次渲染的时间延迟
  • so on…

布局过程

布局是一个从上到下,从外到内进行的递归过程,从根渲染对象,即对应着HTML文档根元素<html>,然后下一级渲染对象,如对应着<body>元素,如此层层递归,依次计算每一个渲染对象的几何信息(位置和尺寸)。

几何信息-位置和尺寸,即相对于窗口的坐标和尺寸,如根渲染对象,其坐标为(0, 0),尺寸即是视口
尺寸(浏览器窗口的可视区域)。

** 每一个渲染对象的布局流程基本如:**

  1. 计算此渲染对象的宽度(width);
  2. 遍历此渲染对象的所有子级,依次:
    2.1 设置子级渲染对象的坐标
    2.2 判断是否需要触发子渲染对象的布局或回流方法,计算子渲染对象的高度(height)
  3. 设置此渲染对象的高度:根据子渲染对象的累积高,margin和padding的高度设置其高度;
  4. 设置此渲染对象脏位值为false。

3:样式、JS 在 HTML 中如何放置?

script标签最好放在</body>标签的前面,因为放在所有body中的标签后面就不会出现网页加载时出现空白的情况,可以持续的给用户提供视觉反馈,同时在有些情况下,会降低错误的发生。而css标签应该放在<head></head>标签之间,因为如果放在</body>标签的前面,那么当DOM树构建完成了,渲染树才构建,那么当渲染树构建完成,浏览器不得不再重新渲染整个页面,这样造成了资源的浪费。效率也不高。如果放在<head></head>之间,浏览器边构建边渲染,效率要高的多。

布局(Layout)或回流(reflow,relayout)

创建渲染树后,下一步就是布局(Layout),或者叫回流(reflow,relayout),这个过程就是通过渲染树中渲染对象的信息,计算出每一个渲染对象的位置和尺寸,将其安置在浏览器窗口的正确位置,而有些时候我们会在文档布局完成后对DOM进行修改,这时候可能需要重新进行布局,也可称其为回流,本质上还是一个布局的过程,每一个渲染对象都有一个布局或者回流方法,实现其布局或回流。

知道操作DOM成本多高了吗?

其实写了这么多,感觉偏题了,大量的资料参考的是chrome开发者文档。感觉js脚本资源那块还是有点乱,包括和DOMContentLoaded的关系,希望大家能多多指点,多多批评,谢谢大佬们。

操作DOM具体的成本,说到底是造成浏览器回流reflow和重绘reflow,从而消耗GPU资源。

构建DOM树

DOM树,即文档内所有节点构成的一个树形结构。
假设浏览器获取返回的如下HTML文档:

<!doctype html>
<html>
<head>
  <link rel="stylesheet" href="./theme.css"></link>
  <script src="./config.js"></script>
  <title>关键渲染路径</title>
</head>
<body>
  <h1 class="title">关键渲染路径</h1>
  <p>关键渲染路径介绍</p>
  <footer>@copyright2017</footer>
</body>
</html>

首先浏览器从上到下依次解析文档构建DOM树,如下:

图片 9

Dom构建.png

1:javascript的诞生

  • 1990年底,欧洲核能研究组织(CERN)科学家Tim Berners-Lee,在全世界最大的电脑网络——互联网的基础上,发明了万维网(World Wide Web),从此可以在网上浏览网页文件。最早的网页只能在操作系统的终端里浏览,也就是说只能使用命令行操作,网页都是在字符窗口中显示,这当然非常不方便。

  • 1992年底,美国国家超级电脑应用中心(NCSA)开始开发一个独立的浏览器,叫做Mosaic。这是人类历史上第一个浏览器,从此网页可以在图形界面的窗口浏览。

  • 1994年10月,NCSA的一个主要程序员Marc Andreessen联合风险投资家Jim Clark,成立了Mosaic通信公司(Mosaic Communications),不久后改名为Netscape。这家公司的方向,就是在Mosaic的基础上,开发面向普通用户的新一代的浏览器Netscape Navigator。

  • 1994年12月,Navigator发布了1.0版,市场份额一举超过90%。

  • 1995年,Netscape公司雇佣了程序员Brendan Eich开发这种网页脚本语言。Brendan Eich有很强的函数式编程背景,希望以Scheme语言(函数式语言鼻祖LISP语言的一种方言)为蓝本,实现这种新语言。

  • 1995年5月,Brendan Eich只用了10天,就设计完成了这种语言的第一版。

  • 1995年12月4日,Netscape公司与Sun公司联合发布了JavaScript语言。

  • 1996年3月,Navigator 2.0浏览器正式内置了JavaScript脚本语言。

绘制(painting)

最后是绘制(paint)阶段或重绘(repaint)阶段,浏览器UI组件将遍历渲染树并调用渲染对象的绘制(paint)方法,将内容展现在屏幕上,也有可能在之后对DOM进行修改,需要重新绘制渲染对象,也就是重绘,绘制和重绘的关系可以参考布局和回流的关系。

CSS 资源阻塞渲染

构建Render树需要DOM和CSSOM,所以HTML和CSS都会阻塞渲染。所以需要让CSS尽早加载(如:放在头部),以缩短首次渲染的时间。

单线程

渲染引擎是单线程工作的,意味着渲染流程是一步一步渐进完成的。

2:触发reflow:

  • width/height/border/margin/padding的修改,如width=778px;
  • 动画,:hover等伪类引起的元素表现改动,display=none等造成页面回流;
  • appendChild等DOM元素操作;
  • font类style的修改;
  • background的修改,注意着字面上可能以为是重绘,但是浏览器确实回流了,经过浏览器厂家的优化,部分background的修改只触发repaint,当然IE不用考虑;

通过网络模块加载到HTML文件后渲染引擎渲染流程如下,这也通常被称作关键渲染路径(Critical Rendering Path):

何时触发reflow和repaint

reflow(回流): 根据Render Tree布局(几何属性),意味着元素的内容、结构、位置或尺寸发生了变化,需要重新计算样式和渲染树;
repaint(重绘): 意味着元素发生的改变只影响了节点的一些样式(背景色,边框颜色,文字颜色等),只需要应用新样式绘制这个元素就可以了;
reflow回流的成本开销要高于repaint重绘,一个节点的回流往往回导致子节点以及同级节点的回流;

GoogleChromeLabs 里面有一个csstriggers,列出了各个CSS属性对浏览器执行Layout、Paint、Composite的影响。

1. CSS和JS在网页中的放置顺序是怎样的?

  1. css放在head标签内,防止渲染时出现白屏
  2. js放在最后body尾部,因为js一般会涉及到一些DOM操作,所以要等全部的dom元素都加载完再加载js,js方法执行时,有可能会阻塞页面加载,如果把js放在前面可能会造成白屏的现象。

流程图:

图片 10

Webkit渲染引擎流程

图片 11

Gecko渲染引擎流程

Webkit浏览器和Gecko浏览器渲染流程大致相同,不同的是:

  1. Webkit浏览器中的渲染树(render tree),在Gecko浏览器中对应的则是框架树(frame tree),渲染对象(render object)对应的是框架(frame);
  2. Webkit中的布局(Layout)过程,在Gecko中称为回流(Reflow),本质是一样的,后文会解释回流的另一层含义–重新布局;
  3. Gecko中HTML和DOM树中间多了一层内容池(Content sink),可以理解成生成DOM元素的工厂。

3.生成render树

DOM树和CSSOM树合并生成render树

图片 12

简单描述这个过程:

DOM树从根节点开始遍历可见节点,这里之所以强调了“可见”,是因为如果遇到设置了类似display: none;的不可见节点,在render过程中是会被跳过的(但visibility: hidden; opacity: 0这种仍旧占据空间的节点不会被跳过render),保存各个节点的样式信息及其余节点的从属关系。

渲染引擎流程

Webkit渲染引擎流程如下图:

图片 13

Webkit.png

Gecko渲染引擎流程如下图:

图片 14

Gecko.png

如上图,Webkit浏览器和Gecko浏览器渲染流程大致相同,不同的是:

  • Webkit浏览器中的渲染树(render tree),在Gecko浏览器中对应的则是框架树(frame tree),渲染对象(render object)对应的是框架(frame);
  • Webkit中的布局(Layout)过程,在Gecko中称为回流(Reflow),本质是一样的,后文会解释回流的另一层含义–重新布局;
  • Gecko中HTML和DOM树中间多了一层内容池(Content sink),可以理解成生成DOM元素的工厂。

页面渲染优化

浏览器对上文介绍的关键渲染路径进行了很多优化,针对每一次变化产生尽量少的操作,还有优化判断重新绘制或布局的方式等等。

在改变文档根元素的字体颜色等视觉性信息时,会触发整个文档的重绘,而改变某元素的字体颜色则只触发特定元素的重绘;改变元素的位置信息会同时触发此元素(可能还包括其兄弟元素或子级元素)的布局和重绘。某些重大改变,如更改文档根元素<html>的字体尺寸,则会触发整个文档的重新布局和重绘,据此及上文所述,推荐以下优化和实践:

1.HTML文档结构层次尽量少,最好不深于六层;
2.脚本尽量后放,放在</body>前即可;
3.少量首屏样式内联放在<head>标签内;
4.样式结构层次尽量简单;
5.在脚本中尽量减少DOM操作,尽量缓存访问DOM的样式信息,避免过度触发回流;
6.减少通过JavaScript代码修改元素样式,尽量使用修改class名方式操作样式或动画;
7.动画尽量使用在绝对定位或固定定位的元素上;
8.隐藏在屏幕外,或在页面滚动时,尽量停止动画;
9.尽量缓存DOM查找,查找器尽量简洁;
10.涉及多域名的网站,可以开启域名预解析

浅析浏览器页面渲染机制

浏览器渲染过程

讨论DOM操作成本,肯定要先了解该成本的来源,那么就离不开浏览器渲染。

这里暂只讨论浏览器拿到HTML之后开始解析、渲染。(怎么拿到HTML资源的可能后续另开篇总结吧,什么握握握手啊挥挥挥挥手啊,万恶的flag…)

  1. 解析HTML,构建DOM树(这里遇到外链,此时会发起请求)
  2. 解析CSS,生成CSS规则树
  3. 合并DOM树和CSS规则,生成render树
  4. 布局render树(Layout/reflow),负责各元素尺寸、位置的计算
  5. 绘制render树(paint),绘制页面像素信息
  6. 浏览器会将各层的信息发送给GPU,GPU将各层合成(composite),显示在屏幕上

一个重要的概念reflow和repaint

  • Repaint
    屏幕的一部分要重画,比如某个CSS的背景色变了。但是元素的几何尺寸没有变。
  • Reflow
    意味着元件的几何尺寸变了,我们需要重新验证并计算Render Tree。是Render Tree的一部分或全部发生了变化。这就是Reflow,或是Layout。(HTML使用的是flow based layout,也就是流式布局,所以,如果某元件的几何尺寸发生了变化,需要重新布局,也就叫reflow)reflow 会从<html>这个root frame开始递归往下,依次计算所有的结点几何尺寸和位置,在reflow过程中,可能会增加一些frame,比如一个文本字符串必需被包装起来。
    Reflow的成本比Repaint的成本高得多的多。DOM Tree里的每个结点都会有reflow方法,一个节点的reflow很有可能导致子子点,甚至父节点以及同级节点的reflow。在一些高性能的电脑上也许还没什么,但是如果reflow发生在手机上,那么这个过程是非常痛苦和耗电的。
    所以,下面这些动作有很大可能会是成本比较高的。
  1. 当你增加、删除、修改DOM结点时,会导致Reflow或Repaint
  2. 当你移动DOM的位置,或是搞个动画的时候。
  3. 当你修改CSS样式的时候。
  4. 当你Resize窗口的时候(移动端没有这个问题),或是滚动的时候。
  5. 当你修改网页的默认字体时。
    注:display:none会触发reflow,而visibility:hidden只会触发repaint,因为没有发现位置变化。

减少reflow/repaint

  1. 不要一条一条地修改DOM的样式。与其这样,还不如预先定义好css的class,然后修改DOM的className。
  2. 不要把DOM结点的属性值放在一个循环里当成循环里的变量。不然这会导致大量地读写这个结点的属性。
  3. 尽可能的修改层级比较低的DOM。当然,改变层级比较低的DOM有可能会造成大面积的reflow,但是也可能影响范围很小。
    为动画的HTML元件使用fixed或absoult的position,那么修改他们的CSS是不会reflow的。
    千万不要使用table布局。因为可能很小的一个小改动会造成整个table的重新布局。

执行JavaScript

上文已经阐述了文档解析时对脚本的处理,我们得知脚本加载,解析和执行会阻塞文档解析,而在特殊情况下样式的加载和解析也会阻塞脚本,所以现在推荐的实践是<script>标签放在</body>标签前面。

5.Paint 绘制

万事俱备,最后只要将确定好位置大小的各节点,通过GPU渲染到屏幕的实际像素。

解析文档(PARSER HTML)

在详细介绍浏览器渲染文档之前,先应该理解浏览器如何解析文档:解析文档的顺序,对于CSS和JavaScript如何处理等。

  1. 解析顺序
    浏览器按从上到下的顺序扫描解析文档;

  2. 解析样式和脚本

  • 脚本

由于通常会在JavaScript脚本中改变文档DOM结构,于是浏览器以同步方式解析,加载和执行脚本,浏览器在解析文档时,当解析到<script>标签时,会解析其中的脚本(对于外链的JavaScript文件,需要先加载该文件内容,再进行解析),然后立即执行,这整个过程都会阻塞文档解析,直到脚本执行完才会继续解析文档。就是说由于脚本是同步加载和执行的,它会阻塞文档解析,这也解释了为什么现在通常建议将<script>标签放在</body>标签前面,而不是放在<head>标签里。现在HTML5提供defer和async两个属性支持延迟和异步加载JavaScript文件,如:
<script defer src="script.js">

  • 改进

针对上文说的脚本阻塞文档解析,主流浏览器如Chrome和FireFox等都有一些优化,比如在执行脚本时,开启另一个线程解析剩余的文档以找出并加载其他的待下载外部资源(不改变主线程的DOM树,仅优化加载外部资源)。

  • 样式

不同于脚本,浏览器对样式的处理并不会阻塞文档解析,大概是因为样式表并不会改变DOM结构。

  • 样式表与脚本

你可能想问样式是否会阻塞脚本文件的加载执行呢?正常情况是不会的,但是存在一个问题是通常我们会在脚本中请求样式信息,但是在文档解析时,如果样式尚未加载或解析,将会得到错误信息,对于这一问题,FireFox浏览器和Webkit浏览器处理策略不同:

  • 当存在有样式文件未被加载和解析时,FireFox浏览器会阻塞所有脚本;
  • 而Webkit浏览器只会阻塞操作了改文件内声明的样式属性的脚本。

1.构建DOM树

<html> <head> <meta name="viewport" content="width=device-width,initial-scale=1"> <link href="style.css" rel="stylesheet"> <title>Critical Path</title> </head> <body> <p>Hello <span>web performance</span> students!</p> <div><img src="awesome-photo.jpg"></div> </body> </html>

1
2
3
4
5
6
7
8
9
10
11
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
    <title>Critical Path</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
  </body>
</html>

无论是DOM还是CSSOM,都是要经过Bytes → characters → tokens → nodes → object model这个过程。

图片 15

DOM树构建过程:当前节点的所有子节点都构建好后才会去构建当前节点的下一个兄弟节点。

执行JAVASCRIPT

上文已经阐述了文档解析时对脚本的处理,我们得知脚本加载,解析和执行会阻塞文档解析,而在特殊情况下样式的加载和解析也会阻塞脚本,所以现在推荐的实践是<script>标签放在</body>标签前面。

4.Layout 布局

有了各个节点的样式信息和属性,但不知道各个节点的确切位置和大小,所以要通过布局将样式信息和属性转换为实际可视窗口的相对大小和位置。

2. 解释白屏和FOUC

白屏现象:

  1. 浏览器需要加载html文件和所有css文件后,构造了渲染树后,再把树节点渲染在屏幕上。如果暂时加载不到css文件,则会阻塞渲染过程,造成白屏现象。
  2. 如果把样式放在底部,对于IE浏览器,在某些场景下(新窗口打开,刷新等)页面会出现白屏,而不是内容逐步展现。
  3. 如果使用 @import 标签(使用@import标签引入样式文件的情况下,会等待html文件加载完成后才加载css文件),即使 CSS 放入 link, 并且放在头部,也可能出现白屏。

FOUC(Flash of Unstyled Content) 无样式内容闪烁:

  1. 如果把样式放在底部,对于IE浏览器,在某些场景下(点击链接,输入URL,使用书签进入等),会出现 FOUC 现象(逐步加载无样式的内容,等CSS加载后页面突然展现样式).对于 Firefox 会一直表现出 FOUC .

JS 资源

  • 阻塞浏览器的解析,也就是说发现一个外链脚本时,需等待脚本下载完成并执行后才会继续解析HTML
    • 这和之前文章提到的浏览器线程有关,浏览器中js引擎线程和渲染线程是互斥的,详见《从setTimeout-setInterval看JS线程》
  • 普通的脚本会阻塞浏览器解析,加上defer或async属性,脚本就变成异步,可等到解析完毕再执行
    • async异步执行,异步下载完毕后就会执行,不确保执行顺序,一定在onload前,但不确定在DOMContentLoaded事件的前后
    • defer延迟执行,相对于放在body最后(理论上在DOMContentLoaded事件前)

DOM 操作成本到底高在哪儿?

2018/04/09 · 基础技术 · DOM

原文出处: palmerye   

从我接触前端到现在,一直听到的一句话:操作DOM的成本很高,不要轻易去操作DOM。尤其是React、vue等MV*框架的出现,数据驱动视图的模式越发深入人心,jQuery时代提供的强大便利地操作DOM的API在前端工程里用的越来越少。刨根问底,这里说的成本,到底高在哪儿呢?

 

举个栗子

<html> <head> <meta name="viewport" content="width=device-width,initial-scale=1"> <link href="style.css" rel="stylesheet"> </head> <body> <p>Hello <span>web performance</span> students!</p> <div><img src="awesome-photo.jpg"></div> <script src="app.js"></script> </body> </html>

1
2
3
4
5
6
7
8
9
10
11
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
    <script src="app.js"></script>
  </body>
</html>

图片 16

  • 浏览器拿到HTML后,从上到下顺序解析文档
  • 此时遇到css、js外链,则同时发起请求
  • 开始构建DOM树
  • 这里要特别注意,由于有CSS资源,CSSOM还未构建前,会阻塞js(如果有的话)
  • 无论JavaScript是内联还是外链,只要浏览器遇到 script 标记,唤醒JavaScript解析器,就会进行暂停 blocked 浏览器解析HTML,并等到 CSSOM 构建完毕,才执行js脚本
  • 渲染首屏(DOMContentLoaded 触发,其实不一定是首屏,可能在js脚本执行前DOM树和CSSOM已经构建完render树,已经paint)

参考文献:

已同步至个人博客-软硬皆施
Github 欢迎star :)

2 赞 4 收藏 评论

图片 17

DOMContentLoaded 和 load

  • DOMContentLoaded 事件触发时,仅当DOM加载完成,不包括样式表,图片…
  • load 事件触发时,页面上所有的DOM,样式表,脚本,图片都已加载完成

什么是DOM

Document Object Model 文档对象模型

什么是DOM?可能很多人第一反应就是div、p、span等html标签(至少我是),但要知道,DOM是Model,是Object Model,对象模型,是为HTML(and XML)提供的API。HTML(Hyper Text Markup Language)是一种标记语言,HTML在DOM的模型标准中被视为对象,DOM只提供编程接口,却无法实际操作HTML里面的内容。但在浏览器端,前端们可以用脚本语言(JavaScript)通过DOM去操作HTML内容。

那么问题来了,只有JavaScript才能调用DOM这个API吗?

答案是NO

Python也可以访问DOM。所以DOM不是提供给Javascript的API,也不是Javascript里的API。

PS: 实质上还存在CSSOM:CSS Object Model,浏览器将CSS代码解析成树形的数据结构,与DOM是两个独立的数据结构

本文由金沙棋牌发布于金沙棋牌官方平台,转载请注明出处:网页的渲染机制,JS相关概念

关键词: