前端性能优化指南

从代码优化到网站的性能优化

原文javascript

咱们都知道前端性能很重要,并尝试从各个方面优化它,但你是否真的知道你网站的性能瓶颈在哪呢?是你的代码执行效率低下,仍是javascript文件过大,是频繁的Dom操做,仍是缓慢的渲染速度?最重要的是你应该知道从哪些方面提高网站的性能瓶颈,并长期观察对比各个方案的效果。css

目录

  • 代码优化
    • 高性能代码
      • 数据存取
      • DOM编程
      • 算法和流程控制
      • ajax
      • 编程实践
    • 构建优化
      • webpack
      • react
      • vue
  • 网站性能
    • 传输过程
      • DNS优化
      • 资源传输优化
    • 渲染过程
      • 懒加载
      • 动画
      • 同构
    • 交互过程
      • dom优化
      • css优化
      • 事件优化

代码优化

代码优化包含两个方面,一是对于JS引擎和浏览器而言,高效率的代码,二是对于不一样构建环境的代码,这部分优化针对不一样的框架而言手段都不尽相同。html

高性能代码 -来自高性能Javascript一书

1、数据存取前端

  • 使用局部变量:在函数中读取局部变量是最快的,读取全局变量是最慢的,由于经过做用域链解析标识符须要开销,而且做用域链越长,开销越大。若是在函数中须要屡次引用全局变量也可优化:
var global = 0;
function() {
    // 在函数内部定义一个变量, 后续直接访问局部变量便可
    var temp = global;
    temp++;temp--;
    console.log(temp);
}
复制代码
  • 缓存对象的成员值: 对象的属性获取须要必定开销,且嵌套越深开销越大,如读取location.href老是比读取window.location.href要快,对于须要频繁访问的深层对象,须要将其缓存:
var name = element.name;
for(var i = 0; i < 10000; i++) {
    name += "zhou";
    // element.name += "zhou";
}
复制代码

2、DOM编程vue

  • 建立DOM节点:在大多数状况下,使用innerHTML要比原生DOM方法快,如createElement
  • querySelect:如querySelectorAll查询语句性能高于其它的查询语句,缘由在于querySelect返回的不是动态的HTML集合
  • 离线DOM:对于批量修改DOM样式时,推荐离线DOM树,减小访问DOM布局的次数。

3、算法和流程控制java

  • 循环
    • for-in循环效率低于for、while, 速度只有for循环的1/7。
    • forEach等基于函数的迭代:forEach会基于数组的每一项调用函数,这是它慢于循环的缘由,在对速度有严格要求或数组较长时不推荐使用
    • for循环优化
// 原始版本
for(let i = 0; i < list.length; i++){
    fn(list[i]);
}

// 优化版本1, 缓存list.length,由于length值是不变的
for(let i = 0, len = list.length; i < len; i++){
    fn(list[i]);
}

// 优化版本2,倒序循环,把i的值做为控制条件,去掉 i < len的比较消耗
for(let i = list.length; i--){
    fn(list[i]);
}
复制代码
  • 条件语句react

    • 将最可能出现的条件前置以减小判断的次数
    • 将扁平化的判断转换为嵌套式的判断,二分法缩小区间,如判断1-10可转换为小于6和大于等于6
    • 经过表查找替代if/else和switch,所谓表查找是将全部状况的返回值存入数组中访问的一种方法
  • 递归webpack

    • 循环代替递归:因为递归调用栈的限制可能会致使栈溢出错误,全部可使用循环改写递归
    • 尾递归优化:尾递归优化不会致使栈溢出,严格模式下才有效
    • 开启缓存:在一次递归中可能相同的值会被计算屡次,将结果缓存是优化递归效率的手段之一。

4、Ajaxcss3

  • 使用get获取数据能够被缓存,只有在url长度大于2048个字符时再考虑post
  • jsonp的方式加载数据是很是快的,由于相应的消息直接做为js代码执行,而不是做为字符串须要进一步处理,但须要注意的是不要使用jsonp去加载不可靠的源数据。
  • 使用图片向服务器发送信息:具体方式为,新建一个图片对象new Image(),并将其src属性设置为服务器的urlsrc = url + params,服务器会接受到数据并保存,使用这种方式的消耗是很是小的。
  • 数据格式:json做为轻量级的数据交换格式是首选,其次是特定字符分割的字符串
  • 使用类库:合理使用ajax类库能够避免在一些古怪的浏览器中遇到问题

5、编程实践web

  • 使用字面量建立对象和数组: var a = [], b = {},能够在控制台中查看效率
  • with和eval:避免使用这两种语句,由于它们会形成动态做用域问题且性能较低。
  • 避免重复工做:不少次的代码重构其实都在在消除重复,尽可能在开发阶段处理好,不要想着重构是再来优化
  • 经过位运算提示计算效率:如,经过对整数求模的方式为表格添加条纹,使用i % 2 === 1判断, 这个计算可转换为位运算提高效率: i & 1
  • 在进行数学运算:使用Math提供的方法要比本身写执行效率高,尽可能使用原生方法
  • 使用性能分析工具:如chrome的devTools分析性能瓶颈

构建优化

1、webpack

  • 代码分离
    • 多入口分离
    • 复用组件分离 (CommonsChunkPlugin)
    • 动态导入组件分离 (import())
  • Loader
  • resolve(解析路径) & Externals(外部扩展)
  • Dll优化
  • source map
  • tree shaking
  • Split CSS(分离css)
  • webpack性能优化

2、React

  • shouldComponentUpdate:合理使用shouldComponentUpdate阻止组件更新能够有很大的性能优化(或使用PureComponent)
  • 为列表设置惟一key:设置惟一key可让diff算法在对比同一级元素变更时有更好的表现(不用频繁的删除和添加元素,而是移动元素位置)
  • React Fragments:避免额外标记,由于每一个组件须要有单一根元素,因此常常新增额外标签,使用React Fragments提供的一组空元素解决: <></>
  • 不使用内联函数:内联函数每次调用“render”函数时都会建立一个新的函数实例
// 一、使用内联函数
render() {
    return (
   	<div onClick={e => this.handleClick(e)}>test</div>
    )
}

// 二、不使用内联函数
render() {
    return (
   	<div onClick={this.handleClick}>test</div>
    )
}
复制代码
  • componentWillMount:在react16更新后,render以前的生命周期有可能会执行屡次(React Fiber的影响),而且在react17中也会删除今生命周期,建议不要使用。重复执行也是getDerivedStateFromProps钩子被设计为static函数的缘由 参考react16的更新
  • 优化条件渲染:不少状况React不须要彻底卸载一个组件,如动态切换显示和隐藏,这时咱们能够考虑用样式切换来代替条件渲染(相似于vue的v-if和v-show)。
  • componentDidCatch:为组件建立错误边界,componentDidCatch钩子能捕获到组件自己render方法和子组件的生命周期方法抛出的错误,为应用提供一个错误收集的方式和兜底的设计。
  • 不要使用index做为key:在某些状况下使用index做为key会致使更糟糕的性能,如在列表头部新增一个项,这会致使列表全部项被从新添加。相同的道理,不要作出将列表尾部的项移动到列表头部等操做。

3、Vue

  • v-if / v-show: 合理使用v-if / v-show
  • 列表元素设置key(同react)
  • keep-alive组件缓存
  • data优化:对于不须要被监听的对象不要放在data中,或者使用Object.freeze()冻结对象。
  • props: 定义尽可能详细的props, 避免这样使用props: ["status"]
  • v-if和v-for: v-if和v-for不要用在一块儿,好的作法是使用计算属性代替

网站性能优化

一般,网站的性能指标反应在网站加载时间,和后续交互体验上。网站从加载到用户可交互的时间有多个过程时间和。

传输过程

用户在访问网站过程当中,会有DNS解析,TCP握手,HTTP资源传输等过程, 针对这些过程优化以下。

  • DNS预解析,对静态资源域名添加dns-prefetch

    • a标签的href在各大主流浏览器中会自动dns-prefetch,可是https域名不会。
    • 添加meta头的方式解决https域名不会自动预解析: <meta http-equiv="x-dns-prefetch-control" content="on">
    • 使用场景一般是网站中包含了大量的外部资源,<link rel="dns-prefetch" href="//wddsss.com/a.jpg">
  • HttpDns防劫持 DNS劫持是运营商的DNS服务在解析后返回了不正确的主机IP,将用户导入到其余网页的现象,可使用HttpDns(某云有提供此服务)。

    image.png
    使用HttpDns eg:http://127.0.0.1?domain=https://www.wddsss.com

  • CDN加速资源传输

    • 物理层的硬件优化:更快的硬盘读取,更高的网络带宽
    • 网络层的寻址优化:寻找距离最近的资源,依赖于网络层的路由协议寻址算法
    • 传输层的优化:1: TCP的慢启动流量控制能够用于避免网络拥塞,2: 设置rwnd为一合理的值提高最大吞吐量(针对不一样类型的流量,rwnd的值设置应该不尽相同)
    • 应用层的缓存优化:资源合理设置缓存
  • SSL加速

    • 减小中间证书:客户端经过https请求服务端,服务端返回证书信息给客户端,客户端须要验证证书是否可信(浏览器的可信CA列表验证),若是证书的颁发机构不在浏览器可信列表中,则会检查此CA的上层机构是否可信,直到找到可信CA。经过减小中间证书机构优化证书验证效率
    • OCSP Stapling:OCSP是一个TLS证书状态查询扩展,它加速上述查询CA过程,能够在服务端配置。
  • Http/2优化资源传输

    • 多路复用:一个域名维护一个TCP连接,http请求以流的方式传输,实现资源并行下载
    • 头部压缩:使用静态表和动态表压缩http的头部信息
  • 资源压缩

    • Brotli压缩算法(用于纯文本): 由Google推出的无损压缩算法。Brotli 经过变种的LZ77算法、Huffman 编码以及二阶文本建模等方式进行数据压缩,与其余压缩算法相比,它有着更高的压缩效率。对于常见的纯文本,Brotli压缩性能比gzip提升了17-25%(IE不支持)
    • gzip:gzip能够对全部常见web资源压缩,浏览器支持性也很好,几乎全部浏览器都支持
  • 图片优化

    • 响应式图片:在不一样环境不一样场景中使用不一样size或者不一样质量的图片,通常CDN图片都会提供此功能。
    • webp图片支持:对于支持的浏览器返回webp格式的图片,注意两个问题,一是向下兼容,对于不支持webp的浏览器须要返回可用格式的图片,二是服务端缓存(memcahe经过vary字段缓存多份html文档解决) 详细请查看全站webp支持
    • gif转循环视频:在浏览器中gif的表现不理想,可使用循环播放的视频代替
    • save-date: 在http请求头中新增save-data: on,获取压缩后的图片(须要服务端支持)
  • 资源缓存

    • 强缓存:由http头信息的Expires/Cache-Control控制,当资源处于强缓存未过时状态,资源的状态码是 200(memory cache or disk cache)
    • 协商缓存: 由http头信息的 ETag/If-None-Match、last-modified/if-modified-since控制,协商缓存未过时状态码是 304(Not Modified),注意过分使用Etag计算文件hash值会增大服务器压力,增长文件相应时间,须要谨慎使用。
    • service workers:使用service workers缓存静态资源甚至整个页面

渲染过程

  • 下载js文件
    • 普通js:同步下载js文件,下载完成后当即执行
    • async:异步下载js文件,下载完毕后当即执行
    • defer:异步下载js文件,在文档加载完毕,DOMContentLoaded事件触发以前执行
    • 动态下载:在js文件中添加一个script标签,并为其添加src属性,当次标签被添加到dom时开始下载,推荐使用。
    • ajax下载:在ajax中获取js文件内容,并设置script.text = rs.jsContent,经过此方式添加的js的主要优势是你能够下载js代码但不当即执行。

async和defer都能异步下载js文件,作到了js下载的同时不阻塞文档解析,可是async因为其加载完成后当即执行的特性,致使js的执行顺序没法控制,因此实战中推荐使用defer。

  • 懒加载:
    • webpack配置路由懒加载:依赖于import()或require.ensure()
    • 组件懒加载:一样依赖于import()或require.ensure()的动态导入
    • IntersectionObserver构造函数: IntersectionObserver为开发者提供了一种能够异步监听目标元素与其祖先或视窗(viewport)交叉状态的手段,说白了就是提供了一种监听目标元素是否在可视区域(viewport)内的api,咱们可使用它实现懒加载,以图片懒加载为例:
const io = new IntersectionObserver(callback);
let imgs = document.querySelectorAll('[data-src]')
imgs.forEach((item)=>{
    io.observe(item)
})
function callback(items){
    items.forEach(item => {
        if(item.isIntersecting){ // 当前元素可见
            item.target.src = item.target.dataset.src
            io.unobserve(item.target)
        }   
    })
}
复制代码

这里图片的初始src能够设置一个质量很低的图片,以达到图片加载的流畅度。

  • 预加载

    • preconnet预链接:浏览器对于要请求的资源都须要进行DNS查询,TCP链接,TSL认证过程,使用preconnet能够预先创建链接,在须要使用时直接获取资源。<link preconnet href="https://www.wddsss.com/a.js"/>
    • prefetch预加载:prefetch可以让浏览器预加载一个资源<link prefetch href="https://www.wddsss.com/a.html" />
    • prerender预渲染: 浏览器不只会下载资源,还会分配部分资源对其渲染,以到达用户下个页面响应速度更快,达到秒开的目的。
  • css优先下载

    • 将首屏使用的css放置在head中提早下载,放置由于css下载过慢致使页面闪烁。
    • 将重要的css代码内嵌在html文档中,缺点是耦合性高,不利于缓存
    • 使用HTTP/2服务端推送传递重要的css
  • 骨架屏:骨架屏就是在页面数据还没有加载前先给用户展现出页面的大体结构,经常使用在比较规则的列表页面,能够本身设计也可以使用已有的解决方案。

  • 动画优化

    • translate3d:开启translate3d可让GPU参与加速动画渲染,这是一种欺骗浏览器的hack方法,让浏览器认为即将渲染3D动画,其实元素根本没有在z轴运动。
    • will-change:will-change是css3新增的属性,和translate3d相似,是一种加速动画渲染的方法,js在异步事件中触发浏览器大量绘制界面时,浏览器每每是没有准备的被动使用cpu去计算页面渲染,而will-change能够提早告知浏览器此元素的渲染须要gpu参与高速渲染。用法:will-change: transform, will-change: contents
    • 使用高性能动画属性:如transformopacity等,减小使用box-shadow等消耗较大的属性动画。
    • 减小回流的动画:元素的某些属性在发生变化后会致使浏览器大面积重绘页面,视觉上反应为动画卡顿。
    • js的执行和渲染互斥:js在执行期间将主线程中的渲染操做收集起来,并在本轮事件循环结束(微任务执行完毕)后执行全部渲染操做,可是若是在js中获取布局信息则会打乱这里过程,迫使浏览器将暂存的全部渲染操做优先执行,而后获取最新的渲染状态,如获取元素的offsetTop、clientTop等。
    • requestAnimationFrame:requestAnimationFrame是H5新增的api,传统的js实现动画在setInterval中实现,setInterval中的代码并非严格意义上的定时执行,有可能形成性能问题,requestAnimationFrame则充分利用了屏幕的刷新机制,可使用此API代替setInterval
  • 服务端渲染

交互过程

  • web worker: 使用web woker处理耗时操做,在执行完成后将结果通知主线程便可,web woker优化可让用户的交互及时响应,不至于被耗时的js执行阻止界面渲染。

  • DOM

    • 缓存dom引用:将获取的dom元素赋值给变量,不用重复获取dom对象
    • 避免在循环中操做dom
    • documentFragment:合并DOM操做
    • class替换:对于元素上样式变化较多的操做能够考虑使用class替换
    • 使用框架:如今前端流行框架都有本身的dom处理方案,建议在理解其原理的同时合理的使用。
  • CSS

    • flexbox:flexbox布局代替浮动布局
    • calc:非特殊状况不使用计算属性
    • 选择器:优先使用class选择器,避免选择器嵌套过深
    • @import:避免使用@import,@import引入的css下载优先级较低,而且兼容性有必定问题。
    • 不用ID选择器:避免使用ID选择器声明样式
  • 事件

    • 防抖和节流
    • 事件委托:相比于为每一个元素绑定事件,事件委托能够减小网站内存消耗,不然在一些低端机型上可能成为性能瓶颈。

说在最后

本次的优化指南从代码的执行到网站的加载,从构建的优化到资源传输,罗列了一组优化清单,你能够经过逐条对比的方式找到本身网站的优化空间。因为篇幅问题,其中多数内容都只是总结输出一笔带过,并不涉及具体如何优化细节,能够自行经过本身搜索或超连接阅读。

性能的优化应该始于一个完善的检测系统,而且有健全的度量过程,且必需要清楚,对于网站的一次优化重构,具体带来了怎样的性能提高?整个优化过程最好是一个有计划且有目标的,你须要清楚这次优化的目的,和目标性能的提高,这不是盲目的。

权衡你的站点,权衡时间和收益,首先列出优先级最高的优化清单,而后就去作吧!

最后给你们推荐一网站,热点检索专用,站长高产似那啥,你想看的站点都有。戳我查看

相关文章
相关标签/搜索