关于硬件加速哪些优秀的资源总结

Posted liangklfang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于硬件加速哪些优秀的资源总结相关的知识,希望对你有一定的参考价值。

问题1:transform动画为什么没有经过大量的重绘?

解答:为什么 transform 没有触发 repaint 呢?(1)简而言之,transform 动画由GPU控制,支持硬件加速,并不需要软件方面的渲染。(2)浏览器接收到页面文档后,会将文档中的标记语言解析为DOM树。DOM树和CSS结合后形成浏览器构建页面的渲染树。渲染树中包含了大量的渲染元素,每一个渲染元素会被分到一个图层中,每个图层又会被加载到GPU形成渲染纹理,而图层在GPU中transform 是不会触发 repaint 的,这一点非常类似3D绘图功能,最终这些使用 transform 的图层都会由独立的合成器进程进行处理。(3)3D 和 2D transform 的区别就在于,浏览器在页面渲染前为3D动画创建独立的复合图层,而在运行期间为2D动画创建。动画开始时,生成新的复合图层并加载为GPU的纹理用于初始化 repaint。然后由GPU的复合器操纵整个动画的执行。最后当动画结束时,再次执行 repaint 操作删除复合图层。(4)如果某一个元素的背后是一个复杂元素,那么该元素的 repaint 操作就会耗费大量的资源,此时也可以使用上面的技巧来减少性能开销(transform)。内容摘自CSS动画之硬件加速  CSS3 Filter的十种特效

问题2:chrome浏览器控制台可能牵涉的渲染过程?

recaculate style:这个过程是根据CSS选择器,比如.headline或.nav > .nav_item,对每个DOM元素匹配对应的CSS样式。这一步结束之后,就确定了每个DOM元素上该应用什么CSS样式规则。

layout:上一步确定了每个DOM元素的样式规则,这一步就是具体计算每个DOM元素最终在屏幕上显示的大小和位置。web页面中元素的布局是相对的,因此一个元素的布局发生变化,会联动地引发其他元素的布局发生变化。比如,<body>元素的宽度的变化会影响其子元素的宽度,其子元素宽度的变化也会继续对其孙子元素产生影响。因此对于浏览器来说,布局过程是经常发生的。

Paint Setup and Paint:本质上就是填充像素的过程。包括绘制文字、颜色、图像、边框和阴影等,也就是一个DOM元素所有的可视效果。一般来说,这个绘制过程是在多个层上完成的

update layer tree:更新RenderLayer树

composite layers:对页面中DOM元素的绘制是在多个层上进行的。在每个层上完成绘制过程之后,浏览器会将所有层按照合理的顺序合并成一个图层,然后显示在屏幕上。对于有位置重叠的元素的页面,这个过程尤其重要,因为一旦图层的合并顺序出错,将会导致元素显示异常。

补充学习使用Chrome DevTools的Timeline和Profiles提高Web应用程序的性能 使用Chrome DevTools的Timeline分析页面性能  使用CSS3开启GPU硬件加速提升网站动画渲染性能   Google Chrome中的高性能网络(一)  Chrome渲染分析之Timeline工具的使用

你可以仔细阅读Progressive Web App Dev Summit来了解CSS/JS->Style->计算样式->布局->绘制->渲染层合并的流程。同时使用transform/opacity实现动画效果一文也明确指出了如何通过上面的内容来提升动画的性能:

(1)只使用transform/opacity来实现动画效果。但是这时候提升动画性能(没有reflow/repaint)是有条件的,也就是动画元素必须独占一个层,这时候可以通过第二点来实现

(2)用`will-change`/`translateZ`属性把动画元素提升到单独的渲染层中
(3)避免滥用渲染层提升:更多的渲染层需要更多的内存和更复杂的管理,同时由于每个渲染层的纹理都需要上传到GPU处理,因此我们还需要考虑CPU和GPU之间的带宽问题、以及有多大内存供GPU处理这些纹理的问题。

 (4)如果transform/opacity无法实现的动画,那么可以参考 FLIP principle

下面属性的修改都会导致回流:


如上图position,font-family,vertical-align,clear,lin-height等都会导致页面的回流。外加上一个clip属性也会导致页面的回流和重绘


如果你动态修改上述任何一个属性都会导致重绘,同时他们所属的层会被传递到GPU中。在移动设备上是昂贵的,因为移动设备的GPU相比于桌面应用要弱小的多,而且CPU和GPU之间的通道有限,因此传输纹理可能需要很长的时间。

动画的良好表现是提升用户体验的法宝,因此我们应该尽量避免对那些会导致重绘和回流的元素进行动画设置,因为他们是昂贵的而且会导致丢帧的问题。最好提前声明动画,因为这样浏览器可以提前对动画进行优化。目前为止transform是最好的设置动画的属性,因此如果你的动画可以使用下列的属性来完成,那么就应该用下面的属性进行替换:
opacity,translate,rotate,scale。原文地址High Performance Animations 译文地址:前端性能优化(CSS动画篇)。当然,还有其他的属性也可能减少页面回流和重绘:

在《你不知道的Z-Index》中有提到,如果某个元素处于以下状态:
当一个元素位于html文档的最外层(元素)
当一个元素position不为initial,并且拥有一个z-index值(不为auto)
当一个元素被设置了opacity,transforms, filters, css-regions, paged media等属性。
(当然还会有其他情况)
那么就会产生一个新的渲染层(我觉得是是堆叠上下文,可以用于不同的composite layer之间的叠加次序),这时候执行动画,只需要GPU按照现有的位图,按照相应的变换在独立的渲染层中输出,然后再合并输出。这个过程并不需要主线程CPU的参与。你也可以阅读前端性能优化之更平滑的动画以及该文章引用的其他文章,但是个人认为,这里的堆叠上下文不是浏览器渲染时候的层的概念,只是元素排列时候堆叠次序。深入理解CSS中的层叠上下文和层叠顺序一文指出,层叠上下文只是为了解释元素发生重叠时候的表现形式,和我们这里说的分层的概念是完全不同的

减少chrome硬件加速的抖动问题:

-webkit-backface-visibility:hidden;
-webkit-perspective:1000;

3.网页的样式计算和布局计算?

renderobject:对于所有的可视节点(script,meta,head等除外)webkit都会建立renderobject对象,该对象保存了为绘制dom节点所必需的各种信息,例如样式布局信息,经过webkit处理后renderobject对象知道如何绘制自己。下面情况都会为dom节点建立renderobject对象:

 dom树的document节点;dom树中的可视节点,如html,div等,webkit不会为非可视节点创建renderobject对象;某些情况下需要创建匿名的renderobject对象,其不对应dom树中任何节点,只是webkit处理上的需要,典型的就是匿名的renderblock节点

renderobject树:这些renderobject对象同dom节点类似,也构成一棵树,称为renderobject树。

注意:renderobject树时基于dom树建立的一颗新树,是为了布局计算和渲染等机制建立的一种新的内部表示.如果dom树中被动态添加了新的节点,webkit也需要创建相应的renderobject对象


注意:从上图可以看出htmldocument节点对应于renderview节点,renderview节点时renderobject树的根节点。同时head元素也没有创建renderobject对象

dom树建立之后->css解析器和规则匹配->renderobject树建立。css解析器和规则匹配处于dom树建立之后,renderobject树建立之前,css解释后的结果会保存起来,然后renderobject树基于该结果进行规范匹配和布局计算。当网页有用户交互和动画等动作的时候通过cssom等技术,js代码同样可以方便的修改css代码,webkit此时需要重新计算样式并重复以上过程。

当webkit创建了renderoject对象后每个对象都是不知道自己的位置,大小等信息的(实际的布局计算在renderobject类中),webkit根据框模型计算他们的位置大小等信息的过程称为布局计算或者排版。布局计算分为两类:第一类是对整个renderobject树进行计算。第二类是对renderobject中某个子树的计算,常见于文本元素活着overflow:auto块的计算,这种情况一般是子树布局的改变不会影响其周围元素的布局,因此不需要计算更大范围内的布局。

布局计算是一个递归的过程,这是因为一个节点的大小通常需要计算他的子女节点的位置大小等信息。步骤如下:

首先,函数(renderobject的layout函数)判断renderobject节点是否需要重新计算。通常需要检查位数组中的相应标记位,子女是否要重新计算等

其次,函数确定网页的宽度和垂直方向上的外边距,这是因为网页通常是在垂直方向上滚动而垂直方向上尽量不需要滚动。

再次,函数会遍历每一个子女节点,依次计算他们的布局。每一个元素会实现自己的layout函数,根据特定的算法来计算该类型元素的布局,如果页面元素定义了自身的宽高。那么webkit按照定义的宽高来确定元素的大小,而对于文字节点这样的内联元素需要结合字号大小和文字的多少来确定对应的宽高。如果页面元素所确定的宽高超出了布局容器包含快所提供的宽高,同时overflow为visible或者auto,webkit会提供滚动条显示所有内容。除非网页定义了页面元素的宽高,一般来说页面元素的宽高实在布局的时候通过计算得到的。如果元素有子女元素那么需要递归这个过程。

最后,节点依据子女们的大小计算的高度得到自己的高度,整个过程结束。那么哪些情况下需要重新计算:

首先,网页首次打开的时候,浏览器设置网页的可是区域,并调用计算布局的方法。这也是一个可见的场景,就是当可视区域发生变化的时候,webkit都需要重新计算布局,这是因为网页块大小发生了变化(rem时候很显然)

其次,网页的动画会触发布局计算,当网页显示结束后动画可能改变样式属性,那么webkit需要重新计算

然后,js代码通过cssom等直接修改样式信息,也会触发webkit重新计算布局

最后,用户的交互也会触发布局计算,如翻滚网页,这会触发新区域布局的计算

注意:布局计算相对比较耗时,一旦布局发生变化,webkit就需要后面的重绘制操作。另一方面,减少样式的变动而依赖现在html5新功能可能有效的提高网页的渲染效率。

4.网页层次和renderlayer树?

 网页是可以分层的,原因之一是方便网页开发者开发网页并设置网页的层次,二是为了webkit处理上的便利,也就是说为了简化渲染的逻辑。webkit会为网页的层次创建相应的renderlayer对象。当某些类型的renderobject的节点或者具有某些css样式的renderobject节点出现的时候,webkit就会为这些节点创建renderlayer对象。一般来说,某个renderobject节点的后代的都属于该节点,除非webkit根据规则为某个后代的renderobject节点创建了一个新的renderlayer对象。

注意:renderlayer树时基于renderobject树建立起来的一颗新树,而且renderlayer节点和renderobject节点不是一一对应关系,而是一对多的关系。下面的情况renderobject对象需要建立新的renderlayer节点(而不是独立的图层,注意下面所说的图层是表示在chrome中有黄色的框包围):

(1)It's the root object for the page
(2)It has explicit CSS position properties (relative, absolute or a transform)
(3)It is transparent
(4)Has overflow, an alpha mask or reflection
(5)Has a CSS filter
(6)Corresponds to <canvas> element that has a 3D (WebGL) context or an accelerated 2D context
(7)Corresponds to a <video> element

下面是《webkit技术内幕的翻译版》:

(1)dom树的document节点对应的renderview节点

(2)dom树中的document的子女节点,也就是html节点对应的renderblock节点

(3)显示的指定css位置的renderobject对象

  注意,下面的CSS不会产生一个独立的层(chrome中没有黄色的框包围),但是会产生一个renderLayer对象。

   div{
     	background-color: #ccc;
     	width:400px;
     	height:400px;
     	position: absolute;
     	left:100px;
     	top:100px;
     	transition:width 5s linear;
     	overflow: hidden;
     }
     .hv{
        width:100px;
     }

(4)有透明效果(transparent)的renderobject对象,如果仅仅设置了一个opacity是不会产生一个独立的层的。这里如果是transparent就只会产生一个renderLayer节点,但是这里却会产生一个图层

    div{
     	background-color: #ccc;
     	width:400px;
     	height:400px;
     	opacity: 0.8;
     	transition:opacity 5s linear;
     	/*有opacity的变化也会产生一个独立的层,这里的transition可以是all也可以是opacity*/
     }
     .hv{
     	 opacity: 0;
     }

(5)有节点溢出(overflow),alpha或者反射等效果的renderobject对象

(6)使用canvas2d和3d(webgl)技术的renderobject对象

    canvas{
     	background-color: #ccc;
     	position:absolute;
     	left:100px;
     	top:100px;
     }
上面这个canvas也不会产生一个独立的图层(但是会产生一个RenderLayer节点),但是如果结合第8点的transform就可以产生图层了

(7)video节点对应的renderobject对象(其他情况参见CSS3硬件加速也有坑!!!)

<video src="http://www.w3school.com.cn/i/movie.ogg"></video>

(8)CSS Transform元素(通过把传递到GPU中的纹理和特定的transform属性结合产生图像就可以了,不需要重绘),这里会产生一个独立的图层,而不仅仅是Renderlayer

 如下:

  transform: translate3d(0,0,0);
   transform:translateZ(0);
  transform: scale3d(1,1,1);
  transform: scaleZ(1);
  transform:rotate3d(0,0,0,0);

transform的scale/translate/rotate的动画也是会产生一个独立的图层的

 

 .container{
  	background-color: #ccc;
  	height:100px;
  	width:100px;
    transform:scale(0.5);
    transition:transform 5s linear;
   /*  这里的scale动画也会产生一个单独的图层,因此不会产生重绘回流等*/
  }
  .hv{
  	transform:scale(1);
  }

如果仅仅设置一个transform其他的属性是不会产生一个独立的图层的,但是如果是一个动画又会产生一个独立的图层,通过动态的为元素添加下面这个running属性同样会产

生一个独立的图层

.running {
  animation: run-around 4s infinite;
}
@keyframes run-around {
  0%{
    transform: translate(0, 0);
  }
  25% {
    transform: translate(200px, 0);
  }
  50% {
    transform: translate(200px, 200px);
  }
  
  75% {
    transform: translate(0, 200px);
  }
}

3D 和 2D transform 的区别就在于,浏览器在页面渲染前为3D动画创建独立的复合图层,而在运行期间为2D动画创建。动画开始时,生成新的复合图层并加载为GPU的纹理用于初始化 repaint。然后由GPU的复合器操纵整个动画的执行。最后当动画结束时,再次执行 repaint 操作删除复合图层。摘抄自CSS动画之硬件加速

注意:filter属性也不会产生一个独立的层,但是会产生一个RenderLayer对象

那些renderLayer具有独立的后端存储?

To make use of the compositor, some (but not all) of the RenderLayers get their own backing surface (layers with their own backing surfaces are broadly referred to as compositing layers). Each RenderLayer either has its own GraphicsLayer (if it is a compositing layer) or uses the GraphicsLayer of its first ancestor that has one. This is similar to RenderObject’s relationship with RenderLayers.(不是每一个RenderLayer都有自己的后端存储的,如果具有自己独立的GraphicLayer的RenderLayer对象就叫做合成层,合成层的概念见文末) 摘抄自GPU Accelerated Compositing in Chrome

什么样的元素才能创建自己独立的后端存储呢(有了独立的后端存储就表示这个层只有该元素本身,这样对transform/opacity动画是绝好的机会,因为创建了独立的图层而且不需要重绘回流,而只有合成就可以了)?在Chrome中至少要符合以下条件之一:
Layer has 3D or perspective transform CSS properties(有3D元素的属性)
Layer is used by <video> element using accelerated video decoding(video标签并使用加速视频解码)
Layer is used by a <canvas> element with a 3D context or accelerated 2D context(canvas元素并启用3D)
Layer is used for a composited plugin(插件,比如flash)
Layer uses a CSS animation for its opacity or uses an animated webkit transform(CSS动画)
Layer uses accelerated CSS filters(CSS滤镜)

div {
    min-height: 380px;
    margin-bottom: 20px;
    background-image: url('./woman.jpg');
    background-size: cover;
    background-repeat: no-repeat;
    filter: blur(60px) saturate(120%) brightness(140%);
    -webkit-tap-highlight-color: rgba(0,0,0,0);/*这个仅仅使用filter是不会产生一个独立的层的,除非结合translate3d等*/
  }
Layer with a composited descendant has information that needs to be in the composited layer tree, such as a clip or reflection(有一个后代元素是独立的layer)
Layer has a sibling with a lower z-index which has a compositing layer (in other words the layer is rendered on top of a composited layer)(元素的相邻元素是独立layer)

以上内容来自:Javascript高性能动画与页面渲染

下面是renderobject树和renderlayer树对应关系


注意:上图renderlayer树应该包含三个renderlayer节点-根节点,子女节点以及叶子节点

除了跟节点,也就是renderlayer节点,一个renderlayer节点的父亲就是该renderlayer节点对应的renderobject节点的祖先链中最近的祖先,并且祖先所在的renderlayer节点同该节点的renderlayer节点不同。基于这个原理,这些renderlayer节点也就构成了一个renderlayer树。每个renderlayer节点包含的renderobject节点都是一个renderobject子树,理想情况下,每个renderlayer对象都有一个后端类,这个后端类涌来存储改renderlayer对象的绘制结果。renderlayer节点可以有效的减少网页的复杂程度,并且在很多情况下能够减少页面重新渲染的开销。注意:renderlayer没有子类,但是renderobject有子类的概念,renderlayer只是表示网页的一个层次,没有子层次的概念。

<!doctype html>
<html>
<head>
  <title></title>
  <style type="text/css">
    video,div,canvas{
      -webkit-transform:rotateY(30deg) rotateX(-45deg);
    }
  </style>
</head>
<body>
 <video src='vidwo.mp4'></video>
 <div>
   <canvas id='a2d'></canvas>
   <canvas id='a3d'></canvas>
 </div>
 <script type="text/javascript">
     var size=300;
     var a2dCtx=document.getElementById('a2d').getContext('2d');
     a2dCtx.canvas.width=size;
     a2dCtx.canvas.height=size;
     a2dCtx.fillStyle='rgba(0,192,192,80)';
     a2dCtx.fillRect(0,0,200,200);
     var a3dCtx=document.getElementById('a3d').getContext('experimental-wbgl');
a3dCtx.canvas.width=size;
a3dCtx.canvas.height=size;
a3dCtx.clearColor(0.0,192.0/255.0,80.0/255.0);
a3dCtx.clear(a3dCtx.COLOR_BUFFER_BIT);
 </script>
</body>
</html>

这个例子就会创建四个层,如下图:


所谓的根层就是跟节点创建的层,它对应着整个网页的文档对象。video对象创建一个层可以有效的处理视频解码器和浏览器之间的交互和渲染。同时需要3d转换的元素也会创建相应的层。位置上根层在最后,层3和层4在最前面。webkit创建新层实际上是为了渲染引擎处理上的方便和高效。下面的例子也会创建两个层:

<!doctype html>
<html>
<head>
  <style>
  div {
    animation-duration: 5s;
    animation-name: slide;
    animation-iteration-count: infinite;
    animation-direction: alternate;
    width: 200px;
    height: 200px;
    margin: 100px;
    background-color: gray;
  }
  @keyframes slide {
    from {
      transform: rotate(0deg);
    }
    to {
      transform: rotate(120deg);
    }
  }
  </style>
</head>
<body>
  <div>I am a strange root.</div>
</body>
</html>
通常,Chrome 会 将一个层的内容在作为纹理上传到 GPU 前先绘制(paint)进一个位图中。如果内容不会改变,那么就没有必要重绘(repaint)。这样处理很好:花在重绘上的时间可以用来做别的事情,例如运行 JavaScript,如果绘制的时间很长,还会造成动画的故障与延迟。Chrome 并不会始终重绘整个层,它会尝试智能的去重绘 DOM 中失效的部分。在本例中,我们修改的 DOM 元素和整个层同样大小。但是在其他众多例子中,一个层内会存在多个 DOM 元素。至于为什么是后面的层更加靠近观察者,请仔细阅读 深入理解CSS中的层叠上下文和层叠顺序

5.webkit的渲染过程?

第一阶段:从网页的url到构建完dom树

具体示意图如下:


注意:网页在加载和渲染过程中会发出domcontent事件和dom的onload事件,分别在dom树构建完成以及dom树构建完并且网页所依赖的资源都加载完成之后。

具体过程如下:

1.网页输入URL时候,webkit调用其资源加载器(总共有三类:特定资源加载器如imageloader,资源缓存机制的资源加载器如cachedresourceloader,通用资源加载器resourceloader)加载该URL对应的网页

2.加载器依赖网页模块建立连接,发起请求并接受回复

3.webkit接受到各种网页或者资源的数据,其中某些资源可能是异步的或者同步的

4.网页被加载给html解释器变成一系列的词语(token)

5.解析器根据词语构建节点node,形成dom树

6.如果节点是js代码的话,调用js引擎解释并执行

7.js代码可能会修改dom树的结构

8.如果节点需要依赖其他资源,例如图片,css,视频等,调用资源加载器加载他们,但是他们是异步的,不会阻碍当前dom树的构建。如果是js资源,那么需要停止当前dom树的构建,直到js资源加载并将被js引擎执行后才继续dom树的构建。我们看看html解析器的解析过程:


第二阶段:从dom树到构建完webkit绘图上下文(webkit利用css和dom树构建renderobject树直到绘图上下文)。具体过程如下:


注意:renderobject树的建立并不表示dom树被销毁,事实上上面四个内部表示结构一直存在,直到网页被销毁,因为他们对于网页的渲染起了很大的作用

1.css文件被css解析器解释成为内部表示结构

2.css解析器工作完成之后,在dom树上附加解释后的样式信息,这就是renderobject树

3.renderobject节点在创建的同时,webkit会根据网页的层次结构创建renderlayer树,同时构建一个虚拟的绘图上下文

第三阶段:从绘图上下文到最终的图像


这一过程主要依赖于2d和3d图像库,具体过程如下:

1.绘图上下文时一个与平台无关的抽象类,它将每个绘图操作桥接到不同的具体实现类,也就是绘图具体实现类

2.绘图实现类也可能有简单的实现,也可能有复杂的实现,在chromium中,他的实现相当复杂,需要chromium合成器来完成复杂的多进程和gpu加速

3.绘图实现类将2d图形库和3d图形库绘制的结果保存下来,交给浏览器来同浏览器界面一起显示

总结:现在的网页很多是动态的网页,这意味着在渲染完成之后,由于网页的动画或者用户的交互,浏览器其实一直在不停的重复执行渲染过程

6.chrome控制台浏览器的加载过程





我们试试navigation.timing


navigationStart

当load/unload动作被触发时,也可能是提示关闭当前文档时(即回车键在url地址栏中按下,页面被再次刷新,submit按钮被点击)。如果当前窗口中没有前一个文档,

那么navigationStart的值就是fetchStart。 

redirectStart

它可能是页面重定向时的开始时间(如果存在重定向的话)或者是0

unloadEventStart:

如果被请求的文档来自于前一个同源(同源策略)的文档,那么该属性存储的是浏览器开始卸载前一个文档的时刻。否则的话(前一个文档非同源或者没有前一个文档)

,为0。

unloadEventEnd:

表示同源的前一个文档卸载完成的时刻。如果前一个文档不存在或者非同源,则为0。

redirectEnd

如果存在重定向的话,redirectEnd表示最后一次重定向后服务器端response的数据被接收完毕的时间否则的话就是0。

fetchStart

fetchStart是指在浏览器发起任何请求之前的时间值。在fetchStart和domainLookupStart之间,浏览器会检查当前文档的缓存。

domainLookupStart

这个属性是指当浏览器开始检查当前域名的DNS之前的那一时刻。如果因为任何原因没有去检查DNS(即浏览器使用了缓存,持久连接,或者本地资源),

那么它的值等同于fetchStart。

domainLookupEnd

指浏览器完成DNS检查时的时间。如果DNS没有被检查,那么它的值等同于fetchStart。

connectStart

当浏览器开始于服务器连接时的时间。如果资源取自缓存(或者服务器由于其他任何原因没有建立连接,例如持久连接),那么它的值等同于domainLookupEnd。

connectEnd

当浏览器端完成与服务器端建立连接的时刻。如果没有建立连接它的值等同于domainLookupEnd。

secureConnectionStart

可选。如果页面使用HTTPS,它的值是安全连接握手之前的时刻。如果该属性不可用,则返回undefined。如果该属性可用,但没有使用HTTPS,则返回0。

responseStart

指客户端收到从服务器端(或缓存、本地资源)响应回的第一个字节的数据的时刻。

responseEnd

指客户端收到从服务器端(或缓存、本地资源)响应回的最后一个字节的数据的时刻。

domLoading

指document对象创建完成的时刻。

domInteractive

指文档解析完成的时刻,包括在“传统模式”下被阻塞的通过script标签加载的内容(除了使用defer或者async属性异步加载的情况)。

domContentLoadedEventStart

当DOMContentLoaded事件触发之前,浏览器完成所有script(包括设置了defer属性但未设置async属性的script)的下载和解析之后的时刻。

domContentLoadedEventEnd

当DOMContentLoaded事件完成之后的时刻。它也是javascript类库中DOMready事件触发的时刻。

domComplete

如果已经没有任何延迟加载的事件(所有图片的加载)阻止load事件发生,那么该时刻将会将document.readyState属性设置为"complete",此时刻就是

domComplete。

loadEventStart

该属性返回的是load事件刚刚发生的时刻,如果load事件还没有发生,则返回0。

loadEventEnd

该属性返回load事件完成之后的时刻。如果load事件未发生,则返回0。

检测用户通过哪种方式来到此页面:

我们有几种方式来打开一个页面,例如,在地址栏输入url,刷新当前页面,通过history的前进后退。这时候 performance.navigation 就派上用场了。这个 API 有

两个属性: