zoukankan      html  css  js  c++  java
  • (O)WEB:前端网站性能优化(原创)

    *从理论、实战编码、实战调试3个方面学习前端性能优化(包括页面加载时间和页面流畅度):

    -------------------------------理论-------------------------------
    *浏览器的完整渲染过程
    ①输入url,发送请求
    ②加载(即下载)整个.html文件
    ③加载完后解析(即运行)html,并在解析的过程中构建DOM树
    ·JavaScript是单线程的。浏览器是多线程的:有的线程负责加载资源,有的线程负责执行脚本,有的线程负责渲染界面。
    ·浏览器按从上之下(深度遍历)的原则解析各个html标签
    ·解析标签的过程就是构建DOM树的过程
    ·解析遇到link、script、img标签时,浏览器会向服务器发送请求资源。
        script加载时不影响其他资源加载,但由于不知道js中的执行内容,所以需要等JS加载并执行完后才会继续解析和渲染。
        script的执行会阻塞html解析、其他下载线程以及渲染线程。
        link加载完css后会解析为CSSOM(层叠样式表对象模型,一棵仅含有样式信息的树)。css的加载和解析不会阻塞html的解析,但会阻塞渲染。
        img的加载不会阻塞html的解析,但img加载后并不渲染,它需要等待Render Tree生成完后才和Render Tree一起渲染出来。未下载完的图片需等下载完后才渲染。
    ④当css解析为CSSOM后,html解析为DOM后,两者将会结合在一起生成Render Tree(渲染树)。
    ⑤Layout(reflow): 计算出Render Tree每个节点的形状和位置。(很耗性能)
    ⑥Painting(repaint):浏览器绘制这些元素的样式,颜色,背景,大小及边框等。(很耗性能)
    ⑦Composite(层叠时):浏览器会将各渲染层的信息发送给GPU,GPU会按照合理的顺序合并图层然后显示到屏幕上。(GPU合成图像,单独线程,更流畅,但耗内存)

    *渲染时长
    大多数电脑显示器的刷新频率是60Hz,大概相当于每秒钟重绘60次,因为小于这个频率,页面的渲染就会出现卡顿现象,影响用户体验。大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于16.6ms。


    -------------------------------编码实战-------------------------------
    *前端加载优化:
    ·使用外联CSS和JS:让浏览器缓存,减少http请求。可利用webpack在引用的资源后面自动增加hash值,实现引用不变的基础上对浏览器缓存的资源进行更新。对于缓存容量小的问题,可以考虑缓存在localStorage。
    ·合并CSS、JS:减少http请求。
    ·按需加载:webpack的打包思路就是从程序逻辑入手:入口文件 => 分析代码 => 找出依赖 => 打包,这样代码里不引用的模块就不可能被打进包里,有效减少体积。
    ·按JS模块加载:例如echart中如果只用k线图就只加载k线模块。
    ·压缩HTML、CSS、JS:减少资源大小。
    ·图标采用base64:减少http请求。
    ·图标采用雪碧图(svg、font-icon):减少http请求,减少资源大小。
    ·图片压缩:picdiet(https://www.picdiet.com/zh-cn),是用JS编码,无大小、尺寸、数量限制,默认可平均减少50%体积,清晰度基本没损失,没有兼容性问题,也可以调节压缩比。
    ·避免图片和iFrame等的空src:减少http请求。
    ·图片:滚屏懒加载。
    ·CDN加速:常用资源使用CDN加速提高资源响应速度,如jquery、echarts。(http://www.bootcdn.cn/)
    ·BigRender首屏渲染优化:html, js, css和图片都放在textarea中懒加载(https://segmentfault.com/a/1190000006744741)。
    ·增加Loading进度条:将加载情况呈现给用户。

    *服务端加载优化:
    ·缓存一切可缓存的资源:http缓存。
    ·使用长Cache,避免304重定向:在移动端网络不稳定的前提下,多一次请求,就多了一部分加载时间。
    ·使用DNS缓存:减少浏览器会在DNS解析中消耗的时间。
    ·启用GZip:GZip是http协议的一部分,用来压缩网页大小的。
    ·图片压缩:默认生成缩略图传前端。
    ·减少Cookie:Cookie会影响加载速度,所以静态资源域名不使用Cookie。
    ·域名解析:减少域名解析次数——减少跨站外部资源的引用。
    ·创建连接:减少连接创建次数——使用Keep-Alive避免重复连接。
    ·等待响应:提高服务器运行速度——提高数据运算及查询速度。

    *HTML、CSS结构优化:
    ·页面的标签越少,页面的加载速度就越快,响应也更加迅速。
    ·css不能阻塞加载,所以将css放在头部,防止白屏和重排重绘造成的内容闪烁现象。
    ·尽量保持class的简短,如:.box:nth-last-child(-n+1) .title,改为:.final-box-title。
    ·用flex布局取代浮动布局。
    ·移除空的CSS规则:空的CSS规则增加了CSS文件的大小,且影响CSS树的执行。
    ·不声明过多的font-size:过多的font-size引发CSS树的效率。
    ·值为0时不需要任何单位:为了浏览器的兼容性和性能,值为0时不要带单位。
    ·display属性会影响页面的渲染,需合理使用:
    ①display:inline后不应该再使用width、height、margin、padding以及float
    ②display:inline-block后不应该再使用float
    ③display:block后不应该再使用vertical-align
    ④display:table-*后不应该再使用margin或者float

    *DOM性能优化:主要是防止重排和重绘
    ·图片、音频和视频的宽高在加载完成之前为0,所以静态资源加载前需规定图片的大小。
    ·尽量避免重设图片大小:多次重设图片大小会引发图片的多次重绘
    ·多使用requestAnimationFrame(待学习)
    ·如果同时添加父元素和子元素,要在内存中先将所以子元素添加到父元素下,将父元素一次性加入DOM树。
    ·如果同时添加多个平级子元素,要先将平级子元素加入文档片段,再将文档片段整体加到页面。具体为:
    ①创建文档片段:var frag=document.createDocumentFragment();文档片段: 内存中临时存储多个平级子元素的虚拟父元素。
    ②将平级子元素,先追加到frag下: 用法同普通父元素
    ③将文档片段,整体添加到页面
    ·先把dom节点display:none;(会触发一次重排)。然后做大量的修改后,再把它显示出来。
    ·clone一个dom节点在内存里,修改之后;与在线的节点相替换。
    ·尽量使用ID选择器,ID选择器是最快的。
    ·每次Dom选择都要计算,缓存他,避免强制同步布局(force reflow)。
    ·对于修改元素多个样式,可以使用cssText属性,避免强制同步布局。例如如下,触发3次重排:
    var el = document.getElementById('myDiv');
    el.style.borderLeft = '1px';
    el.style.borderRight = '2px';
    el.style.padding = '5px';
    改成这样,只需1次重排:
    var el = document.getElementById('myDiv');
    el.style.cssText = 'border-left: 1px; border-right: 2px; padding: 5px;';
    将多个样式变化定义到一个class中,再通过className添加class也行。
    ·动画多使用transform和opacity,他们只会引起合成,不会引起布局和重绘。
    ·如果图层中某个元素需要重绘,那么整个图层都需要重绘。用translateZ(0)手动创建渲染层,减少需渲染的像素数,还可以用GPU加速。但是不要滥用这个属性,否则会大大增加内存消耗。(详情:https://developers.google.cn/web/fundamentals/performance/rendering/simplify-paint-complexity-and-reduce-paint-areas)
    ·不要使用table布局,一个小改动会造成整个table的重新布局。
    ·用css动画而不是js动画:css动画有一个重要的特性,它是完全工作在GPU上。因为你声明了一个动画如何开始和如何结束,浏览器会在动画开始前准备好所有需要的指令;并把它们发送给GPU。而如果使用js动画,浏览器必须计算每一帧的状态;为了保证平滑的动画,我们必须在浏览器主线程计算新状态;把它们发送给GPU至少60次每秒。除了计算和发送数据比css动画要慢,主线程的负载也会影响动画; 当主线程的计算任务过多时,会造成动画的延迟、卡顿。所以尽可能地使用基于css的动画,不仅仅更快;也不会被大量的js计算所阻塞。
    ·减小复合层的尺寸:可以将图片的尺寸减少5%——10%,然后使用scale将它们放大;用户不会看到什么区别,但是你可以减少大量的存储空间。
    ·采用虚拟DOM技术:例如Vue、React等框架就采用了虚拟DOM。

    *script优化:
    ·我们知道脚本加载和运行会阻塞页面,所以应该把<script>标签放到最后。
    ·JS:throttle函数(节流):每XX秒内只执行一次;(https://github.com/jacksplwxy/gecko.js)
    ·JS:debounce函数(防抖):当连续触发函数调用时,在最后一次触发的XX秒以后才开始一次调用。(https://github.com/jacksplwxy/gecko.js)
    ·对于多条件判断,用钩子机制比if-else快。
    ·避免快速连续的布局:
    改善前:function resizeWidth() {
    // 会让浏览器陷入'读写读写'循环
    for (var i = 0; i < paragraphs.length; i++) {
    paragraphs[i].style.width = box.offsetWidth + 'px';
    }
    }
    改善后:var width = box.offsetWidth;
    function resizeWidth() {
    for (var i = 0; i < paragraphs.length; i++) {
    paragraphs[i].style.width = width + 'px';
    }
    }
    ·深入原形链越深,搜索的速度就会越慢。记住,搜索实例成员的过程比访问直接量或者局部变量负担更重,所以增加遍历原形链的开销正好放大了这种效果。
    ·函数作用域链的搜索也会消耗性能,全局变量总是在作用域链的最后,所以耗时最久。最好尽可能使用局部变量。一个好的经验法则是:用局部变量存储本地范围之外的变量值。
    ·成员嵌套越深,访问速度越慢。 location.href 总是快于window.location.href ,而后者也要比window.location.href.toString()更快。如果这些属性不是对象的实例属性,那么成员解析还要在每个点上搜索原形链,这将需要更长时间。
    ·计算简化,如:for(var i=2;i<=Math.sqrt(n);i++){if(n%i==0){return false}}
    ·前面提到每帧的渲染应该在16ms内完成,但在动画过程中,由于已经被占用了不少时间,所以JavaScript代码运行耗时应该控制在3-4毫秒。如果真的有特别耗时且不操作DOM元素的纯计算工作,可以考虑放到Web Workers中执行。


    -------------------------------调试实战-------------------------------

    ①资源请求调试:chrome→F12→network→刷新→分析请求时间过长的资源
    ②渲染调试:chrome→F12→performance→录制→生成报告→对红色性能部分进行分析。中文资源:https://segmentfault.com/a/1190000011516068

     (对于不理解的地方可以百度关键词,基本都有资源可以参考学习。本人后续还会继续补充细节,觉得不错就请点个赞吧!)

  • 相关阅读:
    《Java程序设计》第五周学习总结
    团队建设任务
    《Java程序设计》第四周学习总结
    ML_Review_GMM(Ch10)
    ML_Review_SVM(Ch9)
    ML_Review_LDA(Ch5)
    ML_Review_PCA(Ch4)
    关于Kernel的思考
    ML_Homework_Porject_2_LDA_KNN
    CV_Learn
  • 原文地址:https://www.cnblogs.com/jacksplwxy/p/7913051.html
Copyright © 2011-2022 走看看