天天看點

extjs中js資源緩存政策

浏覽器對靜态檔案的緩存主要是通過cache-control來控制的,cache-control可以設定no-cache,max-age以及must-revalidate等來設定緩存政策。

如果max-age> 0則會在max-age的時間内不通路伺服器,用本地緩存的靜态檔案代替。

如果max-age<=0則會每次都詢問伺服器,資源檔案是否有修改,有則200,無則304。這相當于f5操作。

no-cache表示不理會緩存協商,全部重新加載。這相當于是ctrl+f5操作。

must-revalidate表示必須遵循政策規則。因為浏覽器有時候會提取過期的緩存,設定了這個政策後,浏覽器會嚴格遵循客戶設定的政策。

伺服器在首次應答客戶的request時,會傳回last-modified和etag給浏覽器,浏覽器将cache住這些資訊,下一次request伺服器時就會在header裡帶上if-modified-since和if-non-match資訊,這兩個分别對應last-modified和etag。伺服器會提取對應資源的modified時間和etag來做對比,如果有改變則傳回200并且response last-modified和etag給用戶端,沒變則傳回304不需要改變。

目前tomcat已經支援etag, tomcat是根據檔案的contentlength和lastmodified混合編碼生成串兒。因為last-modified因為隻支援到秒級,是以對于秒内頻繁修改的靜态資源效率會比較低下,etag則很好的避免了這一點。

一般對于靜态資源,伺服器端會通過過濾機制,自動為響應的header裡添加max-age資訊,這樣浏覽器就會在本地cache住這些資源。

我們最近的一個項目,前台使用extjs,使用extjs帶來的成本就是所有的頁面幾乎都是js頁面了。因為靜态js的檔案量大,且我們系統的營運地點非洲網絡條件不太好,帶寬資源比較寶貴,不能承受頻繁的靜态資源請求(這裡需要提一點,即使是web伺服器最終響應不需要更改的304請求,對系統也是一次帶寬開銷,我們也想盡力避免),于是我們想通過cache-contro将js檔案cache在本地。但是這就帶來一個問題,對于緊急釋出的一些前台界面,因為逾時機制會無法及時在系統層面展現。

是以我們必須實作一種機制,在釋出了新的js後,對應的引用該js的地方都要能自動重新整理。是以最簡單的方法就是每次編譯完後生成一個版本号,然後對每個引用js的url都添加上版本号就ok。這樣就可以保證在一次釋出周期内始終隻有一個js版本。

extjs實作了一套動态加載政策,可以通過js語言的方式去直接引入一個資源(ext.require),這和我們平時使用的靜态引用(<script src=" xxx")是完全不同的。是以決定研究一下extjs的動态加載機制。

目前網上有開源的extjs4的源碼,在動态load script時,會讀取config配置資訊,config可以通過loader.setconfig來配置。config裡有兩個參數需要注意,一個是disablecachingparam還有一個是disablecaching,後者代表是否需要為每一個請求都聲稱一個版本資訊,前者是版本資訊的參數名,預設是_dc.

loadscriptfile: function(url, onload, onerror, scope, synchronous) {

            if (isfileloaded[url]) {

                return loader;

            }

            var config = loader.getconfig(),

                nocacheurl = url + (config.disablecaching ? ('?' + config.disablecachingparam + '=' + ext.date.now()) : ''),

                iscrossoriginrestricted = false,

            if (!synchronous) {

                onscripterror = function() {

                    //<debug error>

                    onerror.call(scope, "failed loading '" + url + "', please verify that the file exists", synchronous);

                    //</debug>

                };

                scriptelements[url] = loader.injectscriptelement(nocacheurl, onload, onscripterror, scope);

            } else {

                /**

                * 組裝xmlhttprequest對象,根據浏覽器支援情況初始化activexobject或者xmlhttprquest

                */

                try {

                    xhr.open('get', nocacheurl, false);

                    xhr.send(null);

                } catch (e) {

                    iscrossoriginrestricted = true;

                }

* 僞代碼,跨域失敗,執行onerror回調函數

*/

                else if ((status >= 200 && status < 300) || (status === 304)

                    //<if isnonbrowser>

                    || isphantomjs

                    //</if>

                ) {

                    //調用成功, 記錄調試資訊

           ext.globaleval(xhr.responsetext + debugsourceurl);

                    onload.call(scope);

                else {

                   /**

   * 僞代碼,執行onerror回調函數

   */

                // prevent potential ie memory leak

                xhr = null;

        }

代碼很好了解,同步時直接加載,并且通過eval方法直接執行響應的js檔案(responsetext),異步時則通過injectscriptelement加載,同步還是異步可以通過loader裡的syncmodeenabled來設定,預設是false。

注意下這個方法裡的nocacheurl,根據disablecaching決定要不要為url加上版本資訊,緩存無效時會預設以目前時間為版本号。

injectscriptelement: function(url, onload, onerror, scope, charset) {

            var script = document.createelement('script'),

                dispatched = false,

* 設定onload和onerror的回調函數,主要是通過dispatched參數做幂等性校驗

                * 為了相容ie,script需要支援onreadystatechange事件,這個事件會又多個狀态,是以需要做幂等校驗

            script.type = 'text/javascript';

            script.onerror = onerrorfn;

            charset = charset || config.scriptcharset;

            if (charset) {

                script.charset = charset;

            //相容ie

            if ('addeventlistener' in script ) {

                script.onload = onloadfn;

            } else if ('readystate' in script) {   // for <ie9 compatability

                script.onreadystatechange = function() {

                    if ( this.readystate == 'loaded' || this.readystate == 'complete' ) {

                        onloadfn();

                    }

                 script.onload = onloadfn;

            script.src = url;

            (loader.documenthead || document.getelementsbytagname('head')[0]).appendchild(script);

            return script;

該方法主要是建立了一個script的元素,url是前面方法生成的。

因為我們需要的是基于釋出版本資訊的緩存,是以隻需要将nocacheurl的版本資訊由目前時間戳改成編譯産生的版本資訊即可。

最後,注意,因為extjs6做了比較大的重構,url生成挪到了ext.data.proxy.server裡的buildurl裡:

url = ext.urlappend(url, ext.string.format("{0}={1}", me.getcachestring(), ext.date.now())),搜尋這一行即可。

繼續閱讀