前端性能优化的思考


前端性能的优化一直是前端开发之中的重中之重,技术层出不穷的今日,用户对页面访问速度和响应度的要求也与日俱增。在实际的项目开发中,用户体验就是前端优化的生产力,是前端开发的终极奥义。而要探讨性能优化的时候,往往是多方面的~
首先回顾一下,有关DOM的渲染过程详细介绍:
How Browsers Work: Behind the scenes of modern web browsers

📝有这样一道比较经典的题目,从输入URL按下回车键到看到页面的过程中,发生了什么?

很多人都知道浏览器拿到 URL 后要:解析域名,建立 TCP 连接、SSL 握手、等待后端响应等。其中有个非常容易被忽略的环节: Redirect。 ——《全球redirect性能优化实践

整体的Performance Modal分析图:
常见的Performance Modal分析图
前端对于redirect的耗时统计是不精确的(* fetchStart - navigationStart *),因为浏览器的同源策略规定我们只能获取同域的重定向信息,这个时间更准确地定义为beforeEach,这个时间包括浏览器打开耗时、标签初始化耗时,以及redirect

浏览器生成DOM的解析流程大致如下:
浏览器生成DOM的解析过程
基于上述浏览器的渲染原理,参考《移动H5前端性能优化指南》分别对每个流程中的优化加以剖析

一、加载优化

  • 减少 HTTP 请求:浏览器一般同时响应请求为4个请求(PC 一般为4个,Android 支持4个,IOS 5后可支持6个),所以尽量减少页面的请求数,首次加载同时请求数不能超过4个。(Webpack打包等)
  • 合并 CSS、JavaScript;
  • 合并小图片、 使用 CSS sprite,base-64;
  • 缓存:使用缓存可以减少向服务器的请求数,节省加载时间,所以所有静态资源都要在服务器端设置缓存,并且尽量使用长 Cache (长 Cache资源的更新可使用时间戳)
  • 缓存原理参考;
  • 缓存一切可缓存的资源; 使用长 Cache (使用时间戳更新 Cache); 使用外联式引用 CSS、JavaScript(可使用 localstorage 缓存图片);
  • 压缩HTML、CSS、JavaScript:减少资源大小可以加快网页显示速度,所以要对HTML、CSS、JavaScript 等进行代码压缩,并在服务端设置 GZip 压缩(例如多余的空格、换行符和缩进),自动化工具或在线压缩工具;
  • 启用 GZip
  • 使用首屏加载:首屏的快速显示,可以大大提升用户对页面速度的感知,因此应尽量针对首屏的快速显示做优化
  • 按需加载:将不影响首屏的资源和当前屏幕资源不用的资源放到用户需要时才加载,可以大大提升重要资源的显示速度和降低总体流量。但是这也会导致大量重绘,影响渲染性能
  • LazyLoad
  • 滚屏加载
  • 通过 Media Query 加载 预加载:大型重资源页面(如游戏)可使用增加 Loading的方法,资源加载完成后再显示页面。但 Loading 时间过长,会造成用户流失。 对用户行为分析,可以在当前页加载下一页资源,提升速度
  • 图片压缩:图片是最占流量的资源,因此尽量避免使用它,使用时选择最合适的格式(实现需求的前提下,以大小判断),合适的大小。 使用 智图压缩; 使用其他方式代替图片(CSS3,SVG,IconFont)
  • 使用 Srcset (主要移动端)
  • 选择合适的图片(webP优于JPG,PNG8优于GIF)
  • 选择合适的大小(首次加载不大于1014KB,不宽于640(基于手机的一般宽度));

二、脚本优化

  • CSS 写在头部(阻塞 DOM 渲染,不阻塞加载,内联会阻塞加载),JavaScript 写在尾部或异步(默认阻塞加载和渲染)
  • 避免图片和 iFrame 等的空 Src:空 Src会重新加载当前页面,影响速度和效率
  • 尽量避免重设图片大小:指通过 CSS、JavaScript 等中多次重置图片大小,多次重设图片大小会引发图片的多次重绘,影响性能
  • 图片尽量避免使用 DataURL ,前面提到 DataURL可以减少加载时间,但是 DataURL 没有使用图片的压缩算法文件会变大,并且要解码后再渲染,耗时长,综上应尽量避免

三、CSS优化

  • 尽量避免在 HTML 标签中写 Style 属性
  • 避免 CSS 表达式:CSS 表达式的执行需跳出 CSS 书的渲染,因此请避免 CSS 表达式
  • 移除空的 CSS 规则:空的 CSS 规则增加了 CSS 文件的大小,且影响 CSS 树的执行,所以需移除空的 CSS 规则
  • 使用 flexbox 代替传统的布局模型
  • 正确使用 display 属性:
  • display:inline 后边不应再使用 width、height、margin、padding 以及 float
  • d-isplay:inline-block 后不应该使用 float
  • display:block 后不应该再使用 vertical-align
  • display:table 后不应该再使用 margin 或 float
  • 不滥用 float :float 在渲染时的计算量比较大,尽量减少使用
  • 不滥用 Web 字体:Web字体需要下载,解析,重绘当前页面,尽量减少使用
  • 不声明过多的 font-size: 尽量使用语义化标签的默认字体大小,提高 CSS 树的效率
  • 值为 0 时不需要任何单位
  • 标准化各种浏览器前缀
  • 没前缀应放在最后
  • CSS 动画只用(-webkit- 无前缀 两种即可)
  • 其他前缀为 -webkit- 、-moz- 、-ms- 、无前缀 四种
  • 避免让选择器看起来像正则表达式:高级选择器执行耗时长且不易读懂,避免使用

四、JS优化

  • 减少重绘和回流
  • 避免不必要的 DOM 操作;
  • 尽量改变 Class 而不是 Style ,使用 classList 代替 className ;
  • 避免使用 document.write() ;
  • 减少 drawImage;
  • 缓存 DOM 选择与计算:每次 DOM 选择都要计算,用一个变量保存这个值;
  • 尽量使用事件代理,避免批量绑定事件;
  • 尽量使用 ID 选择器:ID选择器是最快的;
  • Touch 事件优化:使用 touchstart 、touchend 代替 click,但注意 Touch 响应过快,易引发误操作;

五、渲染优化

  • HTML 使用 viewport :viewport 可以加速页面的渲染;
  • 减少 DOM 节点:DOM 节点太多影响页面的渲染,应尽量减少 DOM 节点
  • 动画优化:
    • 尽量使用 CSS3 动画
    • 合理使用 requestAnimationFrame 动画代替 setTimeout (跑在主线程上,一般一秒刷新 60 次,提高动画帧的利用效率),参考文章 requestAnimationFrame & CSS3 animation;
    • 适当使用 Canvas 动画, 5 个元素以内使用 CSS 动画(IOS8可使用webGL);
  • 高频事件优化:Touchmove 和 Scroll 事件可导致多次渲染
  • 使用 requestAnimationFrame 监听帧变化,使得在正确的时间进行渲染;
  • 增加响应变化的时间间隔,减少重绘次数
  • GPU 加速:CSS中以下属性(CSS3 transitions、CSS3 3D transforms、Opacity、Canvas、webGL、Video)来触发 GPU 渲染,但过度使用会引发手机耗电增加。

刚看了一篇淘系技术团队的技术分享《响应至上:打造无卡顿的滚动列表》感觉受益匪浅,对提升响应度有了新的认知。而刚巧昨晚在淘系电话评估的时候,面试官问:你认为项目中比较有挑战性的问题?我回答的便是:下拉加载请求服务数据列表的升级优化问题,但因为我们的方案还存在着一定的弊端,并且我最初因为最近没有回顾项目回答的不是很清晰,导致我的面试很关键的点便没有达到面试官的满意⬇️。后悔没有早一天看这些相关的技术,但是现在学习也不算晚,真正掌握才是目的,不要气馁!🌈

推荐文章:《前端性能优化一篇就够了

关于经典面试题📝浏览器输入URL后发生了什么》,如果感兴趣可以读这篇文章~

按下URL并回车之后浏览器发生了什么

⭐️推荐文章👉 2020.03.22-《深入浅出Vue.js》作者刘博文 - 《搞懂性能优化》


Author: Casey Lu
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source Casey Lu !
评论
 Previous
阿里钉钉前端实习一面 阿里钉钉前端实习一面
一、钉钉面试问题 首先做一下你的自我介绍以及你简历上的项目,以及难点创新点(介绍了大概5min) (在我做介绍的过程中,面试官记录了他想要问的问题 at same time) 回答的项目相对之前清晰,但还是有卡顿,多熟悉复盘项目真的重要!
2020-03-04
Next 
JS中的异步“承诺”——Promise JS中的异步“承诺”——Promise
最近在复习JS的知识,其中JS中的异步是最频繁被提及的,决定结合现阶段的学习来简单地梳理一下个人理解。 JS 是一种典型的单线程语言,这是由它在浏览器上运行的性质所决定的。若在并行的线程中实现,并行的线程上无法实现对DOM的修改,此外发生重
2020-03-01
  TOC