• 售前

  • 售后

热门帖子
入门百科

前端从欣赏器的渲染到性能优化

[复制链接]
快乐人L 显示全部楼层 发表于 2021-8-16 12:22:38 |阅读模式 打印 上一主题 下一主题
目次


  • 问题前瞻
  • 欣赏器渲染

    • 1.欣赏器渲染图解
    • 2.css剖析规则
    • 3.js加载和实行机制
    • 4.图片的加载和渲染机制

  • 性能优化

    • css优化
    • 使用
    • 淘汰资源请求
    • 延长加载图像

  • 大促活动实践

    • 2.1 懒加载与异步加载
    • 2.2.资源整合
    • 2.3预剖析与预加载

  • 总结

问题前瞻

1. 为什么css必要放在头部?
2. js为什么要放在body背面?
3. 图片的加载和渲染会壅闭页面DOM构建吗?
4. dom剖析完才出现页面吗?
5. 首屏时间根据什么来判定?

欣赏器渲染


1.欣赏器渲染图解


欣赏器渲染页面重要履历了下面的步骤:
1.处理 HTML 标记并构建 DOM 树。
2.处理 CSS 标记并构建 CSSOM 树。
3.将 DOM 与 CSSOM 归并成一个渲染树。
4.根据渲染树来结构,以计算每个节点的多少信息。
5.将各个节点绘制到屏幕上。
为构建渲染树,欣赏器大要上完成了下列工作:
从 DOM 树的根节点开始遍历每个可见节点。某些节点不可见(比方脚本标记、元标记等),由于它们不会体现在渲染输出中,以是会被忽略。某些节点通过 CSS 隐藏,因此在渲染树中也会被忽略,比方,上例中的 span 节点---不会出现在渲染树中,---由于有一个显式规则在该节点上设置了“display: none”属性。对于每个可见节点,为其找到适配的 CSSOM 规则并应用它们。发射可见节点,连同其内容和计算的样式
根据以上剖析,DOM树和CSSOM树的构建对于页面性能有非常大的影响,没有DOM树,页面基本的标签块都没有,没有样式,页面也基本是空缺的。以是具体css的剖析规则是什么?js是怎么影响页面渲染的?了解了这些,我们才能对症下药,对页面性能举行优化。

2.css剖析规则
  1. <div id="div1">   
  2.     <div class="a">   
  3.         <div class="b">   
  4.             ...   
  5.         </div>   
  6.         <div class="c">   
  7.             <div class="d">   
  8.                 ...   
  9.             </div>   
  10.             <div class="e">   
  11.                 ...   
  12.             </div>   
  13.         </div>   
  14.     </div>   
  15.     <div class="f">   
  16.         <div class="c">   
  17.             <div class="d">   
  18.                 ...   
  19.             </div>   
  20.         </div>   
  21.     </div>   
  22. </div>
复制代码
  1. <span style="background-color: initial;">#div1 .c .d {}    </span>
  2. .f .c .d {}    
  3. .a .c .e {}    
  4. #div1 .f {}    
  5. .c .d{}
复制代码
从左向右的匹配规则

从右向左的匹配规则

假如css从左向右剖析,意味着我们必要遍历更多的节点。不管样式规则写得多过细,每一个dom结点仍然必要遍历,由于整个style rules还会有别的公共样式影响。假如从右向左剖析,由于子元素只有一个父元素,以是可以大概很快定位出当前dom符不符合样式规则。

3.js加载和实行机制

首先明确一点,我们可以通过js去修改网页的内容,样式和交互等,这一意味着js会影响页面的dom结构,假如js和dom构建并行实行,那么很容易会出现冲突,以是js在实行时肯定会壅闭dom和cssom的构建过程,不论是外部js还是内联脚本。
js的位置是否影响dom剖析?
首先我们为什么提倡把js放在body标签的背面去加载,由于从demo上看无论是放在head还是放在body后加载js,页面domcontentload的时间都是一样的:



我们从图中可以看出js的加载和实行是壅闭dom剖析的,但是由于页面并不是一次就渲染完成,以是我们必要做的是尽量让用户看到首屏的部门被渲染出来,js放在头部,则页面的内容地域还没有剖析到就被壅闭了,导致用户看到的是白屏,而js放在body背面,只管此时页面dom仍然没有剖析完成,但是已经渲染出一部门楼层了,这也是为什么我们比较看重页面的首屏时间。
只有DOM和CSSOM树构建好后并归并成渲染树才能开始绘制页面图形,那是不是把整个DOM树和CSSOM树构建好后才能开始绘制页面?这显然是不符合我们寻常访问页面的认知的,现实上:
为达到更好的用户体验,呈现引擎会力图尽快将内容显示在屏幕上。它不必比及整个 HTML 文档剖析完毕之后,就会开始构建呈现树和设置结构。在不断接收和处理来自网络的别的内容的同时,呈现引擎会将部门内容剖析并显示出来。
具体欣赏器什么时候举行初次绘制?可以检察本文对欣赏器初次渲染时间点的探究。

4.图片的加载和渲染机制

首先我们解答一下上面的问题:图片的加载与渲染会不会壅闭页面渲染?答案是图片的加载和渲染不会影响页面的渲染。

那么标签中的图片和样式中的图片的加载和渲染时间是什么样的呢?
剖析HTML【遇到标签加载图片】 —> 构建DOM树加载样式 —> 剖析样式【遇到背景图片链接不加载】 —> 构建样式规则树加载javascript —> 实行javascript代码把DOM树和样式规则树匹配构建渲染树【遍历DOM树时加载对应样式规则上的背景图片】计算元素位置举行结构绘制【开始渲染图片】
固然把DOM树和样式规则树匹配构建渲染树时,只会把可见元素和它对应的样式规则联合一起产出到渲染树,这就意味有不可见元素,当匹配DOM树和样式规则树时,若发现一个元素的对应的样式规则上有display:none,欣赏器会以为该元素是不可见的,因此不会把该元素产出到渲染树上。

性能优化


css优化

1.尽量淘汰层级
  1. #div p.class {   
  2.     color: red;   
  3. }   
  4.    
  5. .class {   
  6.     color: red;   
  7. }
复制代码
层级淘汰,意味者匹配时遍历的dom就少。
关于less嵌套的誊写规范也基于这个道理。
2.使用类选择器而不是标签选择器
淘汰匹配次数
3.按需加载css
  1. (function(){   
  2.     window.gConfig =  window.gConfig || {};   
  3.     window.gConfig.isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);   
  4.     var hClassName;   
  5.     if(window.gConfig.isMobile){   
  6.         hClassName = ' phone';   
  7.    
  8.         document.write('<link rel="stylesheet" href="https://res.hc-cdn.com/cpage-pep-discount-area-v6/2.0.24/m/index.css" rel="external nofollow"  />');   
  9.         document.write('<link rel="preload" href="//res.hc-cdn.com/cpage-pep-discount-area-v6/2.0.24/m/index.js" rel="external nofollow"  crossorigin="anonymous" as="script" />');   
  10.    
  11.     }else{   
  12.         hClassName = ' pc';   
  13.    
  14.         document.write('<link rel="stylesheet" href="https://res.hc-cdn.com/cpage-pep-discount-area-v6/2.0.24/pc/index.css" rel="external nofollow"  />');   
  15.         document.write('<link rel="preload" href="//res.hc-cdn.com/cpage-pep-discount-area-v6/2.0.24/pc/index.js" rel="external nofollow"  crossorigin="anonymous" as="script" />');   
  16.    
  17.     }   
  18.     var root = document.documentElement;   
  19.     root.className += hClassName ;   
  20.    
  21. })();
复制代码
async 与 defer

[来自https://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html]

使用

       
  • 假如脚本是模块化的而且不依赖于任何脚本,请使用async。   
  • 假如该脚本依赖于另一个脚本或由另一个脚本所依赖,则使用defer。

淘汰资源请求

欣赏器的并发数目有限,以是为了淘汰欣赏器由于优先加载许多不须要资源,以及网络请求和响应时间带来的页面渲染壅闭时间,我们首先应该想到的是淘汰页面加载的资源,可以大概尽量用压缩归并,懒加载等方法淘汰页面的资源请求。

延长加载图像

只管图片的加载和渲染不会影响页面渲染,但是为了尽可能地优先展示首屏图片和淘汰资源请求数目,我们必要对图片做懒加载。
  1. document.addEventListener("DOMContentLoaded", function() {   
  2.     let lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));   
  3.     let active = false;   
  4.    
  5.     const lazyLoad = function() {   
  6.         if (active === false) {   
  7.             active = true;   
  8.    
  9.             setTimeout(function() {   
  10.                 lazyImages.forEach(function(lazyImage) {   
  11.                     if ((lazyImage.getBoundingClientRect().top <= window.innerHeight && lazyImage.getBoundingClientRect().bottom >= 0) && getComputedStyle(lazyImage).display !== "none") {   
  12.                     lazyImage.src = lazyImage.dataset.src;   
  13.                     lazyImage.srcset = lazyImage.dataset.srcset;   
  14.                     lazyImage.classList.remove("lazy");   
  15.    
  16.                     lazyImages = lazyImages.filter(function(image) {   
  17.                     return image !== lazyImage;   
  18.                     });   
  19.    
  20.                     if (lazyImages.length === 0) {   
  21.                     document.removeEventListener("scroll", lazyLoad);   
  22.                     window.removeEventListener("resize", lazyLoad);   
  23.                     window.removeEventListener("orientationchange", lazyLoad);   
  24.                     }   
  25.                 }   
  26.                 });   
  27.    
  28.                 active = false;   
  29.             }, 200);   
  30.         }   
  31.     };   
  32.    
  33.     document.addEventListener("scroll", lazyLoad);   
  34.     window.addEventListener("resize", lazyLoad);   
  35.     window.addEventListener("orientationchange", lazyLoad);   
  36. });
复制代码
大促活动实践


2.1 懒加载与异步加载

懒加载与异步加载是大促活动性能优化的重要本领,直白的说就是把用户不必要大概不会立刻看到的页面数据与内容全都挪到页面首屏渲染完成之后去加载,极限减小页面首屏渲染的数据加载量与js,css实行带来的性能消耗。
2.1.1 导航下拉的异步加载
导航的下拉内容是一块结构非常复杂的html片段,假如直接加载,欣赏器渲染的时间会拖慢页面整体的加载时间:

所有我们必要通过异步加载方式来获取这段html片段,等页面首屏渲染竣事后再添加到页面上,大致的代码如下:
  1. $.ajax({   
  2.     url: url, async: false, timeout: 10000,   
  3.     success: function (data) {   
  4.         container.innerHTML = data;   
  5.         var appendHtml = $('<div class="footer-wrapper">' + container.querySelector('#footer').innerHTML + '</div>');   
  6.         var tempHtml = '<div style="display:none;">' + '<script type="text/html" id="header-lazyload-html-drop" class="header-lazyload-html" data-holder="#holder-drop">' + appendHtml.find('#header-lazyload-html-drop').html() + '<\/script><script type="text/html" id="header-lazyload-html-mbnav" class="header-lazyload-html" data-holder="#holder-mbnav">' + appendHtml.find('#header-lazyload-html-mbnav').html() + '<\/script></div>';   
  7.         $('#footer').append(tempHtml);   
  8.             feloader.onLoad(function () {   
  9.             feloader.use('@cloud/common-resource/header', function () {   
  10.             });   
  11.             $('#footer').css('display', 'block');   
  12.         });   
  13.     },   
  14.     error: function (XMLHttpRequest, textStatus, errorThrown) {   
  15.         console.log(XMLHttpRequest.status, XMLHttpRequest.readyState, textStatus);   
  16.     },   
  17. });
复制代码
2.1.2 图片懒加载
官网的cui套件中已经有lazyload的插件支持图片懒加载,使用方法页非常简单:
  1. 官网的cui套件中已经有lazyload的插件支持图片懒加载,使用方法页非常简单:
  2. <divclass="list">
  3. <imgclass="lazyload"data-src="http://www.placehold.it/375x200/eee/444/1"src="占位图片URL"/>
  4. <imgclass="lazyload"data-src="http://www.placehold.it/375x200/eee/444/2"src="占位图片URL"/>
  5. <imgclass="lazyload"data-src="http://www.placehold.it/375x200/eee/444/3"src="占位图片URL"/>
  6. <divclass="lazyload"data-src="http://www.placehold.it/375x200/eee/444/3"></div>
  7.     ...
  8. </div>
复制代码
从代码我们差不多可以猜出图片懒加载的原理,实在就是我们通过覆盖img标签src属性,使得img标签开始加载时由于没有src的具体图片地址而不去加载图片,比及重要资源加载完之后,通过监听onload的时间大概滚动条的滚动机会再去重写对应标签的src值来达到图片懒加载:
  1. /**   
  2. * load image   
  3. * @param {HTMLElement} el - the image element   
  4. * @private   
  5. */   
  6.     _load(el) {   
  7.         let source = el.getAttribute(ATTR_IMAGE_URL);   
  8.         if (source) {   
  9.             let processor = this._config.processor;   
  10.             if (processor) {   
  11.             source = processor(source, el);   
  12.             }   
  13.    
  14.             el.addEventListener('load', () => {   
  15.             el.classList.remove(CLASSNAME);   
  16.         });   
  17.         // 判断是否是什么元素   
  18.             if (el.tagName === 'IMG') {   
  19.                 el.src = source;   
  20.             } else {   
  21.                 // 判断source是不是一个类名,如果是类名的话,则加到class里面去   
  22.                 if (/^[A-Za-z0-9_-]+$/.test(source)) {   
  23.                     el.classList.add(source);   
  24.                  } else {   
  25.                     let styles = el.getAttribute('style') || '';   
  26.                     styles += `;background-image: url(${source});`;   
  27.                     el.setAttribute('style', styles);   
  28.                     el.style.backgroundImage = source; // = `background-image: url(${source});`;   
  29.                 }   
  30.             }   
  31.    
  32.             el.removeAttribute(ATTR_IMAGE_URL);   
  33.         }   
  34.     }
复制代码
具体的插件代码大家可以检察https://git.huawei.com/cnpm/lazyload。
同时官网的页脚部门也采用了采用别的的加载方式也实现了懒加载的结果,页脚的图片都在css中引用,想要延长加载页脚图片就必要延长加载页脚的css,但是延长加载css造成的后果就是页面加载的一瞬间页脚会由于样式确实而显示繁芜,以是我们可以在css样式加载前强势隐藏掉页脚部门,等css加载完成后,页脚dom自带的display:block会主动显示页脚。(==由于页脚的seo特性没有对其举行懒加载==)
2.1.3 楼层内容的懒加载
基于xtpl自带的懒加载本领,共同pep定制页面模板的逻辑,我们可以实现html的懒加载。在页面初次渲染的时候,只有每个楼层的大要框架和标题等关键信息,假如必要的话可以给默认图片等占位,或设置最小高度占位,防止锚点定位失效。
当页面滚动到该楼层的位置,js代码方会实行,在初始化函数中,对该楼层的html举行加载,渲染,实现楼层图片和html的懒加载,淘汰了首屏时间。
具体代码如下:
  1. <div class="nov-c6-cards j-content">   
  2. </div>
复制代码
  1. public render(){   
  2.     this.$el.find('.j-content').html(new Xtemplate(tpl).render(mockData))   
  3.     ...   
  4. }
复制代码
2.1.4 套餐数据懒加载
套餐数据的加载不停以来都是令人头疼的,本次双十一对于套餐脚本也做了优化,不但对数据举行了缓存,同时也可以在指定的范围举行套餐数据的渲染——和上述所说的楼层懒加载共同,可以做到未展示的楼层,套餐数据不请求,下拉框不渲染,询价接口不调用,在首屏不出现大量套餐的情况下,可以大大提拔首屏加载的性能。

2.2.资源整合

2.2.1.页头页尾资源统一维护
基础模板的优化涉及到资源的归并,压缩与异步加载,dom的延长加载和图片的懒加载。首先我们给出官网基础模板引用的一部门js资源的表格:

这部门js存在问题是分散在pep的各个资产库路径维护,有些压缩了,有些没有压缩,js的加载也基本是次序实行,以是我们对这个部门的js和css资源举行了一个整合,举行的操纵是迁徙,归并,压缩。
建立common-resource仓库去统一维护管理页头页脚及公共资源代码。
2.2.2.归并加载方式雷同的基础功能js并压缩
common.js
  1. import './common/js/AGrid';   
  2. import './common/js/jquery.base64';   
  3. import './common/js/lang-tips';   
  4. import './common/js/setLocaleCookie';   
  5. import './common/js/pepDialog';
复制代码
如上面代码,将官网中用的分散的基础功能js归并成一个common.js,经过伏羲流水线发布,cui套件会主动将js压缩,如许做的结果固然是淘汰官网页面请求资源数,减小资源大小。
2.2.3.资源异步加载
观察2.2.1中的表格可以发现,官网大部门js都是放在头部大概是body后次序加载的,这些资源的加载时间必定是在DOMOnLoad之前

这些js都是会壅闭页面的渲染,导致页面首屏加载变慢,我们必要做的就是通过之前头尾资源的整理得出哪些资源是可以在onload之后去加载的,这些我们就可以把页面加载时不必要实行的js和css全部移到页面渲染完成后去加载,少了这部门的js逻辑实行时的壅闭,页面首屏渲染的时间也会大大降低。
通过cui套件中的feloader插件,我们可以比较便捷的控制js和css加载的机会:
  1. feloader.onLoad(function () {   
  2.   feloader.use([   
  3.     '@cloud/link-to/index',   
  4.     '@cloud/common-resource/uba',   
  5.     '@cloud/common-resource/footer',   
  6.     '@cloud/common-resource/header',   
  7.     '@cloud/common-resource/common',   
  8.     '@cloud/common-resource/prompt.css',   
  9.     '@cloud/common-resource/footer.css',   
  10.   ]);   
  11. });
复制代码
下图可以明显看到js的加载都转移到onload之后了:

2.2.4 图片压缩
除了对设计给出的图片有压缩要求外,我们还通过对一部门不常更新的小图标图片举行base64编码来淘汰页面的图片请求数目。


2.3预剖析与预加载

除了延长加载外,基础模板还举行了诸如dns预剖析,资源预加载的本领来提前剖析dns和加载页面资源。
2.3.1 DNS 预剖析
当用户访问过官网页面后,DNS预剖析可以大概使用户在访问双十一活动页之前提进步行DNS剖析,从而淘汰双十一活动页面的dns剖析时间,提高页面的访问性能,实在写法也很简单:
  1. <link rel="dns-prefetch" href="//res.hc-cdn.com" rel="external nofollow" >
  2. <link rel="dns-prefetch" href="//res-static1.huaweicloud.com" rel="external nofollow" >
  3. <link rel="dns-prefetch" href="//res-static2.huaweicloud.com" rel="external nofollow" >
  4. <link rel="dns-prefetch" href="//res-static3.huaweicloud.com" rel="external nofollow" >
复制代码
2.3.2 preload 预加载
活动页的部门js还使用了preload预加载的方式来提拔页面加载性能,preload的为什么可以达到这种结果,我们必要看下面这段摘录:
Preloader 简介
HTML 剖析器在创建 DOM 时假如碰上同步脚本(synchronous script),剖析器会停止创建 DOM,转而去实行脚本。以是,假如资源的获取只发生在剖析器创建 DOM时,同步脚本的介入将使网络处于空置状态,尤其是对外部脚本资源来说,固然,页面内的脚本偶然也会导致延长。
预加载器(Preloader)的出现就是为了优化这个过程,预加载器通过分析欣赏器对 HTML 文档的早期剖析结果(这一阶段叫做“令牌化(tokenization)”),找到可能包罗资源的标签(tag),并将这些资源的 URL 网络起来。令牌化阶段的输出将会送到真正的 HTML 剖析器手中,而网络起来的资源 URLs 会和资源类型一起被送到读取器(fetcher)手中,读取器会根据这些资源对页面加载速率的影响举行有次序地加载。
基于以上原理,我们对官网相对重要的js资源举行preload预加载,以使得欣赏器可以尽快地加载页面所需的重要资源。
  1. <link rel="preload" href="//res.hc-cdn.com/cnpm-feloader/1.0.6/feloader.js" rel="external nofollow"  as="script"/>
  2. <link rel="preload" href="//polyfill.alicdn.com/polyfill.min.js?features=default,es6" rel="external nofollow"  as="script"/>
  3. <link rel="preload" href="https://res-static3.huaweicloud.com/content/dam/cloudbu-site/archive/commons/3rdlib/jquery/jquery-1.12.4.min.js" rel="external nofollow"  as="script"/>
  4. <link rel="preload" href="//res.hc-cdn.com/cnpm-wpk-reporter/1.0.6/wpk-performance.js" rel="external nofollow"  as="script"/>
  5. <link rel="preload" href="//res.hc-cdn.com/cpage-pep-2019nov-promotion/1.1.15/components/activity-banner/images/banner_mb.jpg" rel="external nofollow"  as="image" media="(max-width: 767px)">
复制代码
优化结果


总结

前端性能优化的方法本领并不但限于文章陈述,官网前端团队还会在前端性能优化的道路上学习更多,探索更多,将华为云官网页面的加载性能做到极致!
以上就是前端从欣赏器的渲染到性能优化的具体内容,更多关于性能优化的资料请关注脚本之家别的干系文章!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作