zoukankan      html  css  js  c++  java
  • 前端性能调优

    前几天,对九霄项目进行了一次大范围的性能优化,效果还不错,首屏从最开始的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,谢谢!
    原文链接地址:前端性能调优

  • 相关阅读:
    Mac下好玩的终端命令
    【bzoj3441】乌鸦喝水
    LeetCode[39]: 组合总和
    打击盗版,支持原创
    状态模式(State)-设计模式
    webpack核心概念
    吴裕雄--天生自然TensorFlow高层封装:Estimator-自定义模型
    吴裕雄--天生自然TensorFlow高层封装:Estimator-DNNClassifier
    吴裕雄--天生自然TensorFlow高层封装:Keras-TensorFlow API
    吴裕雄--天生自然TensorFlow高层封装:Keras-多输入输出
  • 原文地址:https://www.cnblogs.com/freestyle21/p/5116753.html
Copyright © 2011-2022 走看看