標籤:
首先,我得說,這篇文章有點標題黨了,其實內容並沒有標題看起來那麼高大上。其次,本文只是做一個技術方案可能性的探討,並沒有提供完善的解決方案,至多給了一個Demo供參考。
目的
如需轉載,請註明轉自:http://www.cnblogs.com/silenttiger/p/4929841.html
前端效能最佳化,我覺得最主要的目的就兩個:1、提升頁面載入速度;2、節約伺服器資源。
這裡特別提一下節約伺服器資源,很多人在做前端效能最佳化的時候,往往只考慮前端效能的問題,而完全忽視前端的效能最佳化對後端伺服器效能的影響。其實,對於一個網路流量比較大的網站來說,節約伺服器資源就是省錢啊。比如,js檔案、圖片檔案的大小越小,伺服器所需的磁碟IO貸款和網路IO貸款也就越小,自然就可相應省下部分開支了。
現有的方法
如需轉載,請註明轉自:http://www.cnblogs.com/silenttiger/p/4929841.html
前端效能最佳化,我們目前主流的技術方案主要也就兩個:1、合并;2、壓縮;3、緩衝。
舉個例子,一個網站有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
上述的這個效能最佳化方案,我想很多人一眼就可以看出來,其實還存在很多問題。
1、首次載入頁面時,緩衝策略無法發揮作用,拖慢了頁面載入速度。
雖然我們配置了緩衝策略,使得使用者訪問過B頁面一次之後再訪問B頁面是可以從瀏覽器緩衝中直接載入其依賴的a\x兩個js檔案的。但是,如果使用者只訪問過A頁面而沒有訪問過B頁面,此時再訪問B頁面的話,只有a的緩衝能夠生效,而x是沒有緩衝的。
2、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
這個新的方案雖然解決了我們的一些問題,但也並非完美無缺。
1、團隊協作更複雜
以往的前端效能最佳化方案中,我們往往只需要一組前端開發人員參與就行了,可是現在的這個方案,由於需要後端提供使用者行為預測的資料,所以很可能需要後端開發的同學也參與進來。如果網站的使用者資料收集是專門的團隊進行的,那麼很可能還需要這個專門的團隊參與整個方案的設計和實施。這無疑大大增加了團隊協作的複雜度,對專案管理水平的要求進一步提高。
2、對網站首頁沒有任何效果
基於使用者行為預測的最佳化方案,只有在使用者進入網站之後才會生效,如果使用者根本就沒有進入網站,我們就什麼都做不了了,所以,網站的首頁在這種最佳化方案中完全得不到任何好處。而網站的首頁往往又是整個網站中受訪量最大的幾個頁面之一,所以這個問題帶來的影響還是比較大的。
3、需要權衡資料即時性和效能
服務端在返回給前端使用者接下來可能需要訪問的資源的時候,是即時地通過資料庫中的資料計算出各個資源被訪問的機率,還是我們通過某種機制事先計算好然後直接讀取返回給前端?如果是即時計算,可能機率的準確性會更高,但是使用者訪問的曆史資料太多的話,這個即時計算是否會消耗過多的系統資源又是個大問題,而如果我們事先計算好這些資料,當網站頁面更新的時候,若這些計算的機率資料沒有更新,則使用者在訪問我們的網站的時候,就無法享受到預先載入帶來的好處,而且會因為我們放棄了傳統最佳化方式而獲得更糟的使用者體驗,那麼這些機率資料什麼時候才能得到更新又是個問題。
如需轉載,請註明轉自:http://www.cnblogs.com/silenttiger/p/4929841.html
此文完全源於本人的一個腦洞,就是忽然靈光一現,想到了這個效能最佳化的方案。我在網路上嘗試搜尋相關的關鍵字,但是並沒有找到很好的資料,所以我想,難道這還是我首創的?如果真是,那肯定還有很多沒有考慮到的細節和不足,寫出來供大家參考。如果不是,那還請各位過來人不靈賜教分享你的實踐經驗!
如需轉載,請註明轉自:http://www.cnblogs.com/silenttiger/p/4929841.html
基於大資料的使用者行為預測在前端效能最佳化上的應用