Zainking / Blog

使用 Github issues 创建的中文博客

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

前端性能优化

Zainking opened this issue · comments

前端性能优化设计很多方面,今天准备写一篇文章做一个纵览的总结。
文章会从 程序性能优化 资源分配优化 传输性能优化 用户体验优化四个大的角度来解析前端性能优化。

程序性能优化

当代前端有两大驱动的开发模式,分别是以DOM为驱动的传统开发模式和以数据为驱动的MVVM开发模式;这两种开发模式分别面临着各自的性能优化问题,除了针对这两种开发模式的性能优化之外还有一些编程细节的性能优化方法。

以DOM为驱动的性能优化:减少或合并DOM操作

众所周知,DOM操作是前端程序中最耗费资源的编程操作。示例代码如下:

 不好的方式
var elem = $('#elem');
for (var i = 0; i < 100; i++) {
  elem.append('<li>element '+i+'</li>');
}

// 好的方式
var elem = $('#elem' ),
arr = [];
for (var i = 0;  i < 100; i++) {
  arr.push('<li>element ' +i+'</li>' );
}
elem.append(arr. join(''));

事实上,最新的MVVM框架纷纷对自家的文档操作中实现了虚拟DOM,通过Diff算法来减少真实DOM操作的压力。

以数据为驱动的性能优化:优化数据结构

前端MVVM框架以数据为驱动进行开发,则需要对问题抽象出合适的逻辑结构,选择合适的数据结构,再通过高效的算法实现。
比如在实际项目中尽量不要使用以前算法课上学的“冒泡排序 选择排序”,而使用Array.prototype.sort()方法,以V8引擎为例,不严谨的说,它的算法跟数组的长度有关,当数组长度小于等于 10 时,采用插入排序,大于 10 的时候,采用快速排序。时间复杂度在最好的情况下可以达到O(n).

其他一些细节:对大量重复事件使用事件代理 对大量数据计算使用缓存 减少无必要的大量触发事件操作等

如一篇我以前文章事件中所提到的事件代理机制,绑定一个元素使用事件代理比绑定100个小事件更加节约性能。
又有如下例子:

// data.length === 100000
for(var i = 0;i < data.length;i++){
  // do something...
}
//上面的代码没有下面的好
for(var i = 0,len = data.length;i < len;i++){
  // do something...
}

因为data.length在源代码中是通过.next()方法一个一个数出来的,所以可以把它赋值给一个缓存len,以后每次重复需要这个量的时候只需要调用缓存就行了。
还有一些情况,常见于触发滚动事件时,滚动事件每滚动一个像素就会触发一次,为了防止它过于频繁的触发,常常使用setTimeout()方法来降低它调用的频率,每隔一定时间段才触发。

资源分配优化

上海交通大学杨斌老师的《软件工程》课程指出,现代软件发展瓶颈之一在于网络传输速度的瓶颈。这也就意味着在我们设计前端工程的时候要考虑如何根据线性的网络传输速度平均分配资源的展现量来缓解用户的焦虑————事实上这样做如果加载的是动态资源还有助于缓解服务器压力,从未来的角度思考,甚至可能帮助缓解客户端内存和磁盘读写压力。

分页 懒加载

使用分页和懒加载的方法在这里不再赘述了,谷歌一下能够得到一大堆的实现方法。这里主要提示三个点:

  1. 分页和懒加载的单页数据数量要选择适合,满足一个节拍的浏览体验,太短让人感觉没刷两页就加载,影响心流;太多则导致加载时间问题。
  2. 对于懒加载,选择合适的占位元素是关键。因为不选择占位元素,往往会导致文档结构展示没有预期设计稿的架构,如果CSS编写者功力不够,甚至会产生整个文档的错位。
  3. 对于分页,要确认是前端处理分页还是后端处理分页。这往往根据团队规划,服务器处理能力而定。事实上很多问题的处理都应该思考这两点。

单页应用基于路由的懒加载

以Vue为例,有

// import Foo from './Foo.vue'
const Foo = resolve => require(['./Foo.vue'], resolve)

因为webpack的流行,很容易以Promise的形式实现基于路由的懒加载和自动的封装打包。

预加载

所谓“预加载”仅仅是一种思路,有多种实践方法,往往用于图片音乐视频等大的静态资源上。
小到在曝光加载时将加载时间略提前于曝光,大到WebApp先加载后运行,都可以称之为“预加载”。

传输性能优化

前端工程的最典型特征在于他是异步的,互联网化的,所以针对‘传输’这一关键点进行优化往往是最直接,最有效的方法。

选择合适的通信协议

在需要服务器主动发布信息的情况下,尽量使用Websocket而不是Ajax轮询。往往能获得更加的用户通信体验和性能。

压缩合并资源:减小请求内容大小和请求数

最直观的,静态资源的大小和多少直接制约了加载的速度,对静态资源的压缩合并打包也显得必要起来。

早期的前端常常是切图仔在线手动压缩合并js,css,用图片处理软件手动降低图片质量,例如像在下面的网址:
在线 JS/CSS/HTML 压缩
再后来,前端工程化**成型,人们开始使用gulp之类的前端工作流处理工具进行压缩合并处理:
gulp使用:进行压缩合并js、css
到了现在,我们最常用的往往是Webpack来进行模块打包,合并压缩。
不仅如此,Webpack甚至可以实现分块打包,按需加载,代码热修改等高级功能:
基于webpack的前端工程化开发解决方案探索

使用CDN传输静态资源

CDN的全称是Content Delivery Network,即内容分发网络。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度。
————百度百科

及时使用CDN可以有效的减少静态资源加载速度。当然,动态资源是严禁CDN的,当然,CDN还有其他的一些妙用:比如Github禁止百度爬虫爬取GithubPages的内容,这个时候可以选择把自己GithubPages上的内容分发到CDN上,这样就可以正确的爬取CDN上的资源然后定向到你的页面了。

启用服务器压缩方案:gzip等

流行的服务器往往都配备了gzip压缩方案,开启它往往不超过几行配置,却可以降低三分之二的流量消耗。

采用合适的缓存方案

不经常更新的内容尽量定义长时间的缓存时间,不但能减少服务器的压力,还可以优化用户的体验。即使是更新频繁的资源也尽量使用Etag头的方法进行缓存更新来让服务器资源获得有效利用。

用户体验优化

通过良好的设计虽然不能直接提高软件性能,但是可以有效的缓解用户焦虑,提高即时数据利用率等。

采用进度条等手段解决“无响应错觉”

进度条最初的设计是用来提醒操作者“我们在进行一些后台的操作,并不是死机了”,直到现在这一经典设计仍旧被沿用。不确定的等待时间比已知的、有限的等待时间让人觉得更长。在一些情况下,并不适用动画加载,如加载H5,上传文件,人们会因无法预知加载时间长短而感到烦躁。你应该给你的用户一个清晰地等待时间,让用户盯着一个下载进度条会让跳出率降低。

采用提前展现内容等方法

即使内容不能点击,也把获得的信息先呈现给用户之后再加载内容或点击事件,这样的操作增加了信息流在时间线性程度上的价值。

优秀的交互设计

我个人最喜欢的交互设计框架当属蚂蚁金服的Antd,下面提供一个链接以飱各位Antd设计语言

综合优化策略————优先补充短板

一只木桶能盛多少水,并不取决于最长的那块木板,而是取决于最短的那块木板。也可称为短板效应。任何一个前端架构,可能面临的一个共同问题,即构成工程的各个部分往往是优劣不齐的,而劣势部分往往决定整个工程的水平。因此,每个人都应思考一下“短板”的位置,并尽早补足它。

附:前端性能测试工具网站

WebPageTest