前端性能调优

前几天,对九霄项目进行了一次大范围的性能优化,效果还不错,首屏从最开始的899个请求4.98s加载,到最后29个请求998ms加载,提升了79%以上。这里记录一下优化的过程,方便有需要的朋友。

开篇

新工作已经4个多月,从0开始到现在三个子系统并行,每个子系统负责滴滴业务线的数据分析,业务量和业务逻辑相当复杂。不过随着业务的扩展,性能也随之会成为阻碍系统运转的瓶颈,性能优化势在必行。

现状

目前前端技术架构是:

seajs + arale + koa + gulp + less

页面部署是:

 + router.js
      - entry[入口]
      - express[快车]
      - hitch[顺风车]
      - daijia[代驾]

页面是由路由依次进入四个页面。由于是单页面应用,所以资源文件都是一次加载,导致子系统越多,加载文件越多,网站也越来越慢。

a

优化

优化一: 按需加载

从上面的架构来看,seajs模块combo在一起,合成一个 nsky.min.js 的原始发布方案已经不能满足需求了,需要划分子系统按需加载。

按需加载的意思是,进入首页时加载首页必须依赖的模块,不加载其他页面(快车,顺风车,代驾)所需资源。

seajs请求资源的原理是正则扫描每个模块中require的字符串,从而提取模块的依赖。加载模块时,首先加载其依赖文件,因此我们能够通过 combo 的方式把模块合并到一个文件。另外,seajs中提供 require.async 来实现异步加载。

因此,我们在router里面转发到不同页面的时候使用 require.async,从而实现分子系统按需加载。

优化结果如下:

b

首屏请求数量直接从899个优化到57个,结果还是可喜。

优化二:请求合并

观察请求瀑布流,发现请求文件还是太多,比如系统资源加载(class.js/events.js/overlay.js等),还有业务代码较多,我们可以把这些资源合并在一起,发出combo请求。

比如发出

http://d.kuaidadi.com/path/??class.js,events.js,overlay.js,xxx.js

然后服务端响应此 combo 请求,返回 class.js, events.js, overlay.js, xxx.js的合集,这样就可以实现请求合并。

首先,需要浏览器发出类似 ??a.js,b.js,c.js 的combo请求。seajs发出combo请求很简单,通过seajs combo插件就可以了:

 seajs.config({
    preload: "seajs-combo"
 });

其次,需要服务器端响应类似 ??a.js,b.js,c.js 的combo请求。

seajs combo插件中文文档中提到,服务端实现combo请求是通过Nginx-Http-Concat实现。

但是由于线上服务器是通过 Nginx 来实现数台生产机器间负载均衡,通过 Nginx Proxy Pass实现非80端转发到80端口。经过试验发现,Nginx的 Proxy PassNginx-Http-Concat不能同时使用,其作者也说过暂时不支持。,所以此路不通。

因此,只能自己实现Node Combo。于是又找了不少node-combo项目(比如node-combo),研究其实现思路,准备自己写koa-node-combo插件了。

后来搜索npm仓库,发现有多个这样的插件,尝试发现koa-combo-parse可行:

app.use(comboParse({
    base: path.resolve(__dirname, '../client/')
}));

但是,问题又来了。combo到一起后系统仍然不能运行,因为文件之间没有识别的id。需要寻找工具来对每个模版补全id和依赖(deps),combo后就能互相识别了。即需要从:

define(function(require, exports, module){
    var a = require('a'),
        b = require('b'); 
    module.exports = {};
});

变成:

define('moduleId', ['a', 'b'], function(require, exports, module){
    var a = require('a'),
        b = require('b');
    module.exports = {};
});

在此过程中调研了 spmgulp-seajs-combo等,发现都不满足需求。

于是自己实现了node小工具 iddeps,并写成了gulp插件gulp-ids-deps

最终,前端发出combo请求后,后端响应combo请求,效果如下:

c

请求数量从57减少到37,请求时间从1.4s减少到了1.18s。

优化三:模版文件预编译

从上面的瀑布流能看出,tpl文件既大又多,占用不少请求时间。

在handlebars中,有预编译的思想,可以在运行前把模版文件预编译成为 js 文件。

业界没有相应的工具,于是自己写了一个node小工具tpl-2-js,并改写成gulp插件gulp-tpl-2-js

Handlebars预编译的过程是:

   //  node
   var preCompileData = handlebars.precompile(data);


   // web 
   var Handlebars = require('handlebars'),
    template = Handlebars.template;

   template(preCompileData)({data});

预编译的过程,是Handlebars precompile创建了一个函数,把每个template存储在 Handlebars.templates里面。

更多详细请看:gulp-tpl-2-js

经过模版预编译后、Combo后,效果如下:

d

可以看到,请求结果从上次的37减少到29,请求时间从1.18s减少到998ms

后续

整体结果优化的效果差不多了,请求数变到最小,时间首屏时间也符合期望。

后续的优化,可以考虑通过 html5 的 appcache 缓存不经常的更新资源文件,把 200 的资源文件变成 302。

本文是记录自己优化系统的心路历程,有不同意见或者有所收获的可以留言讨论。

原创文章,欢迎转载。转载请注明:转载自Fs21 ' s Home,谢谢!
原文链接地址:前端性能调优

原文地址:https://www.cnblogs.com/freestyle21/p/5116753.html