首先,我得说,这篇文章有点标题党了,其实内容并无标题看起来那么高大上。其次,本文只是作一个技术方案可能性的探讨,并无提供完善的解决方案,至多给了一个Demo供参考。javascript
目的php
如需转载,请注明转自:http://www.cnblogs.com/silenttiger/p/4929841.htmlhtml
前端性能优化,我以为最主要的目的就两个:一、提高页面加载速度;二、节约服务器资源。前端
这里特别提一下节约服务器资源,不少人在作前端性能优化的时候,每每只考虑前端性能的问题,而彻底忽视前端的性能优化对后端服务器性能的影响。其实,对于一个网络流量比较大的站点来讲,节约服务器资源就是省钱啊。好比,js文件、图片文件的大小越小,服务器所需的磁盘IO贷款和网络IO贷款也就越小,天然就可相应省下部分开支了。java
现有的方法jquery
如需转载,请注明转自:http://www.cnblogs.com/silenttiger/p/4929841.html数据库
前端性能优化,咱们目前主流的技术方案主要也就两个:一、合并;二、压缩;三、缓存。后端
举个例子,一个网站有A,B,C,D四个页面,分别须要引用a\b, a\b\c, a\b\c\d, a\d这几个js文件。因而咱们考虑到a这个js文件在四个页面中均有引用,因此不参与合并。而后把b\c两个js文件合并成x,把b\c\d三个js文件合并为y。如今A,B,C,D四个页面对js文件的引用规则变成了分别引用a\b, a\x, a\y, a\d这几个js文件。接下来,咱们将a\b\x\y\d这五个js文件分别混淆压缩。浏览器
经过以上一系列的处理,如今用户经过浏览器访问咱们的站点的时候,在A\B\C\D四个页面都只须要发起两个对js文件的请求。同时,四个页面还能够共享对a这个js文件的缓存。缓存
现有的问题
如需转载,请注明转自:http://www.cnblogs.com/silenttiger/p/4929841.html
上述的这个性能优化方案,我想不少人一眼就能够看出来,其实还存在不少问题。
一、首次加载页面时,缓存策略没法发挥做用,拖慢了页面加载速度。
虽然咱们配置了缓存策略,使得用户访问过B页面一次以后再访问B页面是能够从浏览器缓存中直接加载其依赖的a\x两个js文件的。可是,若是用户只访问过A页面而没有访问过B页面,此时再访问B页面的话,只有a的缓存可以生效,而x是没有缓存的。
二、b\x\y\d这四个js文件的内容存在冗余,浪费了服务器资源。
x包含了b\c两个js文件的内容,但当用户使用浏览器请求了x以后再请求b,任然须要从新下载整个b文件,这里对x的缓存是没法使用在b上面的。
从这两个问题来看,彷佛咱们还有进步的空间!
新方法
如需转载,请注明转自:http://www.cnblogs.com/silenttiger/p/4929841.html
针对上面的问题,咱们一个个来解决。
首先是首次加载页面时缓存策略没法发挥做用的问题。其实这个问题也是本文的核心,个人解决方案是预加载。也就是说,当用户尚未访问B这个页面的时候,咱们就预先让用户的浏览器加载B页面所依赖的x这个js文件。
我设计了一个前端资源预加载系统,包括前端js代码、后端预加载策略逻辑,还有用于计算加载策略的数据库。仍是用上面的那个例子。假设A页面是网站的首页,当用户访问A页面后,前端js将用户的SessionID和当前页面的URL发送到后端,并由后端逻辑将这条访问行为记录到数据库的visit_sequence表,而后收集前端资源造成资源列表,包括页面中引用的link、script等,并计算此资源列表的MD5发日后端进行比对。后端逻辑根据页面URL在page_resource_signature表中查找相应的MD5值,若是没有找到,就要求前端js代码发送整个资源列表以及页面URL和资源列表的MD5值并记录到page_resource_signature和page_resource表中;若是根据页面URL找到记录但MD5值不匹配,则要求前端js代码发送整个资源列表以及页面URL和资源列表的MD5值并并更新page_resource_signature和page_resource两个表中的数据;若是根据页面URL找到记录且MD5值也匹配,则后端程序根据数据库中visit_sequence表和page_resource表的记录,计算出用户在当前页面下访问系统中其余页面资源的可能性,并返回给前端代码逻辑,接下来,前端代码预加载后端返回的预加载资源列表中的资源。
前端代码:
/* desc: performancecollector依赖于jquery及md5.js,用于收集用户在系统中各个页面间跳转的路径,以及每一个页面所引用的静态资源列表 */ if(typeof performance !== 'undefined' && typeof performance.timing !== 'undefined'){ $(document).ready(function(){ //统计页面ready时间,并将用户的SessionID和当前页面的URL发送到后端 $.post('http://127.0.0.2/index.php/Home/VisitSequence/Insert/', { SessionID: document.cookie.substr(document.cookie.indexOf('PHPSESSID=') + 10, 26), PageUrl: window.location.href.indexOf('#') < 0 ? window.location.href : window.location.href.substring(0, window.location.href.indexOf('#')), Cost: performance.timing.domContentLoadedEventStart - performance.timing.responseStart }); //收集页面资源信息 var resources = []; $('link').each(function(){ resources.push($(this).attr('href')); }); $('script[src]').each(function(){ resources.push($(this).attr('src')); }); //计算resource的MD5并发往服务器比对 setTimeout(function(){ var resourceSignature = md5(JSON.stringify(resources)); $.post('http://127.0.0.2/index.php/Home/VisitSequence/CompareSignature/', { Signature: resourceSignature, PageUrl: window.location.href.indexOf('#') < 0 ? window.location.href : window.location.href.substring(0, window.location.href.indexOf('#')) }, function(data){ //若是没找到此页面资源的签名,就安排延时上传页面资源签名及资源列表 if(data.find === 0){ $.post('http://127.0.0.2/index.php/Home/VisitSequence/UpdateResource/', { Signature: resourceSignature, Resources: resources, PageUrl: window.location.href.indexOf('#') < 0 ? window.location.href : window.location.href.substring(0, window.location.href.indexOf('#')) }); }else{ //安排延时预加载资源 loadResource(data.resources); } }); }, Math.random() * 1000 + 2000); //预加载资源 function loadResource(resources){ //在这个方法里面加载resources参数中列出的资源 console.log('loadResource', resources); } }); }
数据库表结构:
序列图:
上述这个流程中,最关键的步骤就是“计算各个页面资源被访问的可能性”这一步了,也就是序列图中标红的部分。这个动做能够经过用程序分析用户以往的浏览记录来实现。好比咱们常见的Piwik系统中,就直接提供了每一个页面的上下游关系:
如上图,咱们能够经过Piwik的接口清晰的看到,访问index.php这个页面以后,有37%的用户接下来会访问xxxx/xx=attendance&menuid=19这个页面,还有20%的用户接下来会访问xxxx/xx=ast&a=index&menuid=30这个页面。加入这两个页面都引用了sharelib.js这个文件,那么用户访问index.php这个页面后,须要访问sharelib.js这个资源的可能性就高达57%,那么咱们是否是就可让index.php的前端代码预先加载sharelib.js这个资源呢?这样当用户真的发生页面跳转去浏览别的页面的时候,极可能跳转后的页面所需的前端资源咱们已经预先加载过了,浏览器能够直接从缓存中读取相应的数据,从而实现加快页面加载速度的效果!
由此,经过详细记录用户浏览站点的行为,并分析每一个页面的资源引用状况,咱们就能够实在在用户访问某个页面以前就预先判断出那先资源是值得预先加载的。从而实现资源预加载的效果。
咱们再来看第二个问题,也就是资源合并致使的内容冗余问题。
其实,当咱们解决第一个问题的时候,第二个问题也就不复存在了。由于咱们能够在用户进入页面以前就预先加载页面的资源,因此前端资源的合并也就没有存在的必要了,也就不存在因资源整合致使的内容冗余问题了。
新问题
如需转载,请注明转自:http://www.cnblogs.com/silenttiger/p/4929841.html
这个新的方案虽然解决了咱们的一些问题,但也并不是天衣无缝。
一、团队协做更复杂
以往的前端性能优化方案中,咱们每每只须要一组前端开发人员参与就好了,但是如今的这个方案,因为须要后端提供用户行为预测的数据,因此极可能须要后端开发的同窗也参与进来。若是站点的用户数据收集是专门的团队进行的,那么极可能还须要这个专门的团队参与整个方案的设计和实施。这无疑大大增长了团队协做的复杂度,对项目管理水平的要求进一步提升。
二、对站点首页没有任何效果
基于用户行为预测的优化方案,只有在用户进入站点以后才能生效,若是用户根本就没有进入站点,咱们就什么都作不了了,因此,网站的首页在这种优化方案中彻底得不到任何好处。而网站的首页每每又是整个站点中受访量最大的几个页面之一,因此这个问题带来的影响仍是比较大的。
三、须要权衡数据实时性和性能
服务端在返回给前端用户接下来可能须要访问的资源的时候,是实时地经过数据库中的数据计算出各个资源被访问的几率,仍是咱们经过某种机制事先计算好而后直接读取返回给前端?若是是实时计算,可能几率的准确性会更高,可是用户访问的历史数据太多的话,这个实时计算是否会消耗过多的系统资源又是个大问题,而若是咱们事先计算好这些数据,当站点页面更新的时候,若这些计算的几率数据没有更新,则用户在访问咱们的站点的时候,就没法享受到预加载带来的好处,并且会由于咱们放弃了传统优化方式而得到更糟的用户体验,那么这些几率数据何时才能获得更新又是个问题。
如需转载,请注明转自:http://www.cnblogs.com/silenttiger/p/4929841.html
此文彻底源于本人的一个脑洞,就是突然灵光一现,想到了这个性能优化的方案。我在网络上尝试搜索相关的关键字,可是并无找到很好的资料,因此我想,难道这仍是我独创的?若是真是,那确定还有不少没有考虑到的细节和不足,写出来供你们参考。若是不是,那还请各位过来人不灵赐教分享你的实践经验!
如需转载,请注明转自:http://www.cnblogs.com/silenttiger/p/4929841.html
欢迎关注个人微信公众号:老虎的小窝