zoukankan      html  css  js  c++  java
  • 如何写出高性能网页

    如何写出高性能网页

      很多时候,我们可以在进入某些网站时,发现其响应非常的缓慢,如何不是自身的网速问题,那么很有可能就是网站自身的性能问题了。 

     

    第一部分:网页是如何生成的

      网页生成的过程可以分为下面几个过程:

    1. HTML/SVG/XHTML代码转化成DOM Tree
    2. CSS代码转化成CSSOM,即CSS Object Model。
    3. JavaScript脚本通过DOM APICSSOM API分别来操作DOM Tree和CSS Object Model。
    4. 结合DOM和CSSOM,生成一颗渲染树(包含每个节点的视觉信息)。显然这里的渲染数是不同于DOM树的,比如元素的属性为display:none;的元素就不会出现在渲染树上。
    5. 生成布局(layout),也就是flow的过程,它会将所有渲染树的所有节点进行平面合成,但这是还只是生成布局。
    6. 布局绘制(paint)在屏幕上。

      在这6个步骤中,前面的4步的速度都非常快,只有后面两步,即生成布局(flow)绘制(paint)是非常耗时的,这两个过程合成为“渲染”(render)。

      

    第二部分:重排和重绘

      我们一定听过重排(reflow)和重绘(repaint)这两个词,但是它们是什么,区别在哪呢?

      之前说到,flow和paint合成渲染(render),应当知道,下面这三种情况会导致网页重新渲染

    1. 修改DOM
    2. 修改样式表
    3. 用户事件(比如鼠标点击、输入框输入文字等)

      重新渲染,就需要重新生成布局和重新绘制。前者成为reflow(重排),后者称为repaint(重绘)。值得注意的是:

      1.重排一定会触发重绘,因为重排是先于重绘发生的。

      2.重绘不一定会触发重排,比如只是改变某个元素的颜色,布局不会改变,就不会重排,只有重绘。

      

    第三部分:重新渲染对于性能的影响

      第一部分已经提到:重新渲染是最为耗时的过程,所以如果想要提高网页性能,就需要尽量少地触发重新渲染。

      值得注意的是:虽然DOM和CSS的变动会导致重新渲染,但是浏览器并不会DOM和CSS变动一次就重新渲染一次,而是尽量将所有的变动集中在一起,排成队列,然后一次性执行,这样重新渲染的次数就会大大减少。举例如下:

    element.style.color = 'red';

    element.style.marginTop = '30px';

    这里虽然对于element有两次变动,按照固有的思路即执行了第一条,那么会使其颜色发生改变,发生耗时的重绘,接着js执行第二条语句,布局发生了改变即发生重排,进而导致了重绘,但事实上,浏览器并不是这样乖乖地执行操作的,而是接受到第一个重新渲染的命令之后,并没有立即执行,而是等待(非常短暂的时间)观察下一个命令是否是重新渲染,如果是,就将它们都排成一个队列(FIFO),直到最后一个命令不再是重新渲染,就一次性执行,这样就避免了多次的重新渲染导致的耗时问题。

      但是上面浏览器自行将命令排成队列是有前提条件的,即读操作和写操作不能放在同一条语句中。如下:

    element.style.color='red';

    element.style.marginTop = (parseInt(element.style.marginTop)+10) + "px"  ; 

      在这两条语句中,因为第二条中出现了读操作,所以读操作就阻断了队列,使得第一个命令立即导致重新渲染,第二个命令在读取后发生另一次重新渲染。但是如果我们先执行了读操作,再执行剩下的两个写操作就有可能会避免这样的问题出现,如:

    var marginTop = parseInt(element.style.marginTop);

    element.style.color = 'red';

    element.style.marginTop =marginTop + "px"  ; 

      这样,本身读操作是不会导致重新渲染的,而后面的两个写操作会放进一个队列中只会导致一次重新渲染。

      也就是说,我们应当尽量将写操作时需要用到的读操作尽可能全部放在前面、放在一起,而写操作放在后面使其加入一个队列。但是这仍不是最好的办法,下面会介绍到。

      

    第四部分:提高性能的九个技巧  

      1. 之前提到过的,DOM的多个读操作或写操作应当放在一起,不要在两个读操作之间,加入一个写操作。

      2. 如果某个样式是通过重排得到的,那么最好缓存结果,避免下一次用的时候,浏览器又要重排。 

      3. js中不要一条一条地改变样式,这是最蠢的方式,而是尽量通过改变class,如果不能改变class,就改变cssText属性,这样就可以一次性的改变样式。如下所示:

    // bad
    var left = 10;
    var top = 10;
    el.style.left = left + "px";
    el.style.top  = top  + "px";
    
    // good 
    el.className += " theclassname";
    
    // good
    el.style.cssText += "; left: " + left + "px; top: " + top + "px;";

       我们可以看到使用el.style.cssText是非常强大的,只是需要使用+来拼接字符串会显得有些麻烦。而className的方式可以很好的体现出结构和样式分离的最佳实践。

      4. 尽量使用离线DOM,而不是真实的网页DOM,来改变元素样式,比如,先操作Document Fragment(碎片,孤儿)对象,完成后再把这个对象加入DOM,再比如使用cloneNode()方法,再克隆的节点上进行操作,然后再用克隆的节点替换原始节点。

      5. 先将元素设置位display:none;(需要一次重排和重绘),然后这个元素就不会出现在渲染树上了,此时对这个节点进行100次操作,最后再恢复显示(又需要一次重排和重绘),但是这样一来,你就仅仅使用了两次重新渲染取代了可能高达100次的重新渲染!

      6. position 属性设置为absolute或fixed的元素,重排的开销会比较小,因为不用考虑它对其他元素的影响

      7. 只有在必要的时候,才将元素的display属性设置位可见,因为不可见元素不影响重排和重绘,另外,visibility:hidden的元素只会对重绘有影响,不影响重排。

      8.使用虚拟DOM的脚本库,比如React等。

      9. 使用window.requestAnimationFrame()、window.requestIdleCallback()这两个方法调节重新渲染。

      

    第五部分:刷新率

      scroll事件的回调函数和网页动画就会导致密集的刷新率。网页动画的每一帧(frame)都是一次重新渲染。 每秒低于24帧的动画,人眼就能感受到停顿,一般的网页动画,需要达到30帧到60帧的频率,才能比较流畅。大多数显示器的刷新频率是60Hz,这也就意味着如果网页动画能够做到每秒钟60帧,就会跟显示器同步刷新,达到最佳的视觉效果,这就需要在一秒内60次重新渲染,每次重新渲染的时间不能超过16.66毫秒。

      而一秒之内能够完成多少次重新渲染,这个指标就被称为“刷新率”,英文为FPS(frame per second),60次重新渲染,就是60FPS。

      如果想达到60帧的刷新率,就意味着JavaScript线程每个任务的耗时,必须少于16毫秒。一个解决办法是使用Web Worker,主线程只用于UI渲染,然后跟UI渲染不相干的任务,都放在Worker线程。

        

    第六部分:开发者工具的Timeline面板

      Timeline面板是查看刷新率的最佳工具。

    更多关于Timeline和动画制作提高流畅性的技巧看这里

      

  • 相关阅读:
    IOS中彻底删除mysql方法
    (iOS)Storyboard/xib小技巧
    (iOS)关于GCD死锁的问题
    android-通知Notification
    android-多线程
    android-服务Service
    android-OptionMenu
    android-SQLite 和 Content
    android-数据持久化
    Russia
  • 原文地址:https://www.cnblogs.com/zhuzhenwei918/p/6388954.html
Copyright © 2011-2022 走看看