js腳本加載總結


  這段時間工作工作上不是很緊,零星的在研究瀏覽器的一些東西,剛好這個月又一次輪到我做沙龍講座了,想好了好久,就來一次js腳本加載的總結吧!這一塊應該對於很多做項目的朋友來會有所幫助吧!

  1、js起源

  總所周知網頁最開始的形態是靜態的(也就是所謂的靜態網頁),那時候的網頁主要用於瀏覽資料信息,可是隨着用戶需求的增加,用戶希望在頁面上做一些交互操作,比如頁面需要驗證才能訪問、頁面輸入的一些數據希望下次還可以訪問等等,js的出現滿足了人們的需求。也就出現了所謂的動態網頁。

  雖然js給人們帶來了很好的交互性,但是起初人們亂用腳本,只是網頁頁面代碼混亂不堪,當css的出現解決了這一問題:

  

  知道如今,我們的網頁也是分為三部分:HTML(主要放置界面元素)、CSS(主要負責界面布局)和JS(主要負責界面交互)。

  大家都知道這三部分,但是有很多人不注意一些細節,導致做出的網頁訪問時效率低下。在這里我將和大家討論一下關於js的一些知識,希望給大家帶來一定幫助吧!

  2、三種使用腳本方式

  2.1內部引用JavaScript

  2.11通過HTMLscript標簽加載JavaScript代碼

  如:  

<head>
    <script type="text/javascript">
        document.write("Hello World !");
    </script>
</head>

  2.12通過注釋隱藏JavaScript代碼

  如:

<head>
    <script type="text/javascript">
        <!--
        document.write("Hello World !");
        //-->
    </script>
</head>

  <!-- ... //-->當瀏覽器不支持JavaScript時,屏蔽JavaScript代碼。這個代碼是駭客技術,<!-- ... -->於HTML注釋,// 是JavaScript注釋。當瀏覽器支持JavaScript時//代碼生效,因此HTML的注釋沒有效果;當瀏覽器不支JavaScript時,//代碼無效,因此屏蔽了<!-- ... -->之間的JavaScript代碼。現在這種隱藏JavaScript代碼的方式可以忽略,因為沒有瀏覽器不支持JavaScript,除了部分用戶手動禁止瀏覽器的JavaScript功能,但是這種情況很少發生。

  2.1.3使用noscript標簽為用戶提供更好的體驗

  如:  

<body>
    <script type="text/javascript">
        document.write("Hello World !");
    </script>
    <noscript>
        <p>如果您想查看此網頁,則必須啟用JavaScript。
              然而,JavaScript 似乎被禁用,要么就是您的
              瀏覽器不支持 JavaScript。請更改您的瀏覽器
              選項以啟用 JavaScript,然后刷新。
        </p>
    </noscript>
</body>

  通過JavaScript注釋的方式可以隱藏JavaScript代碼,通過noscript標簽可以為用戶提供更好的體驗(提示用戶你的瀏覽器不支持JavaScript)。

  2.2外部引用JavaScript

  使用<script>標簽的src屬性來加載js腳本。通常JavaScript文件可以使用script標簽加載到網頁的任何一個地方,但是標准的方式是加載在head標簽內。為防止網頁加載緩慢,也可以把非關鍵的JavaScript放到網頁底部。 

<script type="text/javascript" src=“SuperMap.js"></script>

  這里有幾點好處:

  1)避免使用<!-- ... //-->,駭客技術。

  2)統一定義JavaScript代碼,方便查看,方便維護。

  3)使代碼更安全,可以壓縮,加密單個JavaScript文件。

  4)瀏覽器可以緩存JavaScript文件,減少寬帶使用。

  2.3內聯引用JavaScript

  內聯引用是通過HTML標簽中的事件屬性實現的。

<input type="button" value="點我" onclick="alert('你點擊了一個按鈕');">

  上面示例將調用input標簽的onclick屬性,彈出一個提示框。

  3內外腳本的比較

  內聯腳本方式使用場景很少,幾乎沒什么優勢。

  內部腳本示例:http://stevesouders.com/hpws/inlined.php

  外部腳本示例:http://stevesouders.com/hpws/external.php

  內部腳本示例只有一個HTML文檔,其大小為87kb,所有的js和css都包含在HTML文檔自身中。外部腳本示例包含一個HTML文檔(7kb)、一個樣式表(59kb)和三個腳本(1kb、11kb和9kb),總計87kb。盡管所需下載的總數據量是相同的,內部腳本示例還是比外部示例快30%到50%。這主要是因為外部示例需要承擔多個HTTP請求帶來的開銷。盡管外部腳本示例可以從樣式表和腳本的並行下載中獲益,但一個HTTP請求與五個HTTP請求之間的差距導致內部腳本示例更快一些。

  盡管結果如此,現實中還是使用外部文件會更合理一些,因為外部文件所帶來的收益--------js文件有機會被瀏覽器緩存起來。HTML文檔--------至少是那些包含動態內容的HTML文檔--------通常不會被配置為可以進行緩存。當遇到這種情況時(HTML沒有被緩存),每次請求HTML文檔都要下載內部的js。另一方面,如果js是外部文件,瀏覽器就能緩存它們,HTML文檔的大小減小,而且不會增加HTTP請求的數量。

   關鍵因素是,與HTML文檔請求數量相關的、外部js組件被緩存的頻率。這個因素盡管難以量化,但可以通過下面的手段進行衡量:

  3.1頁面查看

  每個用戶產生的頁面查看越少,內部js的論據越強勢。想象一個普通用戶每個月只訪問你的網站一次。在每次訪問之間,外部js文件很可能從瀏覽器的緩存中移除。另一方面,如果普通用戶能夠產生很多的頁面查看,瀏覽器很可能將外部Js文件放在緩存中。使用外部文件提供js帶來的收益會隨着用戶每月的頁面查看次數或用戶每會話產生的頁面查看次數的增長而增加。

  3.2空緩存VS完整緩存

  在比較內部和外部文件時,知道用戶緩存外部組件的可能性這一點非常重要。我們在Yahoo!進行了測量,發現每天至少攜帶完整緩存訪問Yahoo!功能一次的用戶占40%到60%。同樣的研究表明,具有完整緩存額的頁面查看數量占75%到85%。注意第一個統計測量的是“唯一用戶”而第二個是“頁面查看”。具有完整緩存的頁面查看所占的百分比比攜帶完整緩存的唯一用戶的百分比高,這是因為很多用戶在一次會話中進行了多次頁面查看。每天,用戶可能只有開始的一次訪問攜帶的是空緩存,之后的多次后續頁面查看都具有完整緩存。如果你的網站的本質上能夠為用戶帶來高完整緩存率,使用外部文件的收益就更大。如果不太可能產生完整緩存,則內部腳本是更好的選擇。

  3.3組件重用

  如果你的網站中的每個頁面都使用了相同的js,使用外部文件可以提高這些組件的重用率。在這種情況下使用外部文件更加具有優勢,因為當用戶在頁面間導航時,js組件已經位於瀏覽器的緩存中了。相反的情況也很容易理解--------如果沒有任何兩個頁面共享相同的js,重用率就會非常低。難的是絕大多數網站不是非黑即白的。這就帶來一個單獨相關的問題--------當把js打包到外部文件中時,應該把邊界划在哪里?

  

  在典型情況下,頁面之間的js的重用即不可能100%重疊,也不可能100%無關。在這種中間情形中,一個極端就是為每個頁面提供一組分離的外部文件。這種方式的缺點在於,每個頁面都強制用戶使用另外一組外部組件並產生令響應時間變慢的HTTP請求。這種方式對於普通用戶只訪問一個頁面和很少進行跨頁訪問的網站來說是有意義的。

  另一個極端是創建一個單獨的、聯合了所有js的文件。這只要求用戶生成一個HTTP請求,但它增加了用戶首次進行頁面查看時的下載數據量。在這種情況下,用戶瀏覽頁面時要下載的js多於所需的數量。而且,在任何一塊獨立的腳本改變后,都需要更新這個文件,使所有用戶已經緩存了的當前版本無效。這種情況對於那些每月會話數量較高、普通用戶在一個會話中訪問多個不同頁面的網站來說是有意義的。

  如果你的網站不符合這兩種極端情況,最好的答案就是折中。將你的頁面划分成幾種頁面類型,然后為每種類型創建單獨的腳本,這比維護一個單獨的文件要復雜,但通常比為每個頁面維護不同的腳本要容易,並且對於給定的任意頁面都只需要下載很少的多余的js。

  最后你做出的與js外部文件的邊界相關的決定影響着組件的重用程度。如果你可以找到一個平衡點,實現較高的重用性,那么外部文件的論據更強勢一些。如果重用度很低,還是內部腳本更有意義些。

  在對於內部和外部腳本進行比較分析時,關鍵點在於與HTML文檔請求數量相關的外部js組件被緩存的頻率。在此我介紹了三種基准(頁面查看、空緩存VS完整緩存和組件重用),這有助於你確定最好的選擇。對於任何網站來說,正確答案都依賴於這些基准。

  大家如果還想更詳細的了解瀏覽器腳本、css等的一些效率問題,可以看《高性能網站建設指南》,那里面的14條具體的優化原則的確很精辟。

  4 將腳本放在底部

  4.1腳本帶來的問題

  下面是一個腳本放在中部的示例

  http://stevesouders.com/hpws/js-middle.php

  經過編程的腳本下載需要很長時間,因此很容易看到問題--------頁面的下半部分要花很長時間才能顯示。出現這一現象是因為腳本阻塞了並行下載。在回顧了瀏覽器如何並行下載之后,我們再回過頭解決這一問題。

  4.2並行下載

  對響應時間影響最大的是頁面中組件的數量。當緩存為空,每個組件都會產生一個HTTP請求,有時即便緩存是完整的亦是如此。要知道瀏覽器會並行地執行HTTP請求,你可能會問,為什么HTTP請求的數量會影響響應時間呢?瀏覽器不能一次將它們都下載下來嗎?

  對此的解釋要回到HTTP 1.1規范,該規范建議瀏覽器從每個主機名並行下載兩個組件。很多web頁面需要從一個主機名下載所有的組件。查看這些HTTP請求會發現它們是呈階梯狀的,如圖所示:

  

                      圖4.1

 如果一個Web頁面平均地將其組件分別放在兩個主機名下,整體響應時間將可以減少大約一半。HTTP請求的行為看起來會是圖4.2所示

  

                        圖4.2

  此處可以並行下載四個組件(每個主機名兩個)。為了對頁面加載變快的現象給出可視的效果,其中每個時間塊的橫向寬度和圖4.1是一樣的。

  每個主機名並行下載兩個組件的限制只是一個建議。默認情況下瀏覽器都遵守這一建議,但用戶也可以重寫該默認設置。但增加並行下載數量並不是沒有開銷的,其優劣取決於你的寬帶和CPU速度。

  4.3腳本阻塞下載

  並行下載組件的優點是很明顯的。然而,在一些比較舊的瀏覽器在下載腳本時並行下載實際上是被禁用的--------即使使用了不同的主機名,瀏覽器也不會啟動其他的下載。其中一個原因是,腳本可能使用document.write來修飾頁面內容,因此瀏覽器會等待,以確保頁面能夠恰當的布局。(現在的瀏覽器雖然可以並行下載,但是同樣阻塞布局)在下載腳本時瀏覽器阻塞並行下載的另一個原因是為了保證腳本能夠按照正確的順序執行。如果並行下載多個腳本,就無法保證響應是按照特定順序到達瀏覽器的。例如:后面的腳本比頁面中之前出現的腳本更小,它可能首先執行。如果它們之間存在着依賴關系,不按照順序執行就會導致js錯誤。

  如下是一個例子:

  http://stevesouders.com/hpws/js-blocking.php

  該頁面按照順序包含下列組件

    1、來至host1的一個圖片

    2、來至host2的一個圖片

    3、來至host1的一個加載需要大約10秒的腳本

    4、來至host1的一個圖片

    5、來至host2的一個圖片

  4.4最差情況:將腳本放在頂部

  至此,腳本對Web頁面的影響就清楚了:

    1、腳本會阻塞對其后面內容的呈現

    2、腳本會阻塞對其后面組件的下載

  如果將腳本放在頁面頂部--------正如通常情況那樣--------頁面中的所有東西都位於腳本之后,整個頁面的呈現和下載都會被阻塞,直到腳本加載完畢腳本放在頂部的示例:

  http://stevesouders.com/hpws/js-top.php

  由於整個頁面的呈現被阻塞,因此導致了白屏現象。逐步呈現對於良好的用戶體驗來說是非常重要的,但緩慢的腳本下載延遲了用戶所期待的反饋。

  4.5最佳情況:將腳本放在底部

  放置腳本的最好地方時頁面的底部。這不會阻止頁面內容的呈現,而且頁面中的可是組件可以盡早下載。腳本放在底部的示例:

  http://stevesouders.com/hpws/js-bottom.php

  把兩個頁面--------腳本放在頂部的和腳本放在底部的--------並列放在一起瀏覽,其對比更為突出。可以在下面這個示例中看到這一點:

  http://stevesouders.com/hpws/move-scripts.php

  4.6正確地放置

  前面那些示例是使用了大概需要10秒才能下載完的腳本。希望你使用的腳本不需要這么長時間的延遲,但一個腳本很可能花費比預期長的時間,用戶的寬帶也會影響腳本的響應時間。你的頁面中的腳本所產生的影響可能沒有這里展示的那么嚴重,但仍需要注意。在頁面中包含多個腳本也會帶來問題。

  在很多情況下,很難將腳本移到底部。例如,如果腳本使用document.write向頁面中插入了內容,就不能將其移動到頁面中靠后的位置。

  經常出現的另外一種建議是使用延遲腳本。Defer屬性表明腳本不包含document.write,瀏覽器得到這一線索就可繼續進行呈現。從下面的示例可以看到這一點:

  http://stevesouders.com/hpws/js-defer.php

  但是不保險,有一些老的瀏覽器不能識別defer,所以最好還是將腳本放於底部。

 5動態加載腳本

  詳見我之前的博客

js動態加載腳本

  6三種實用方式

  6.1異步批量添加外部腳本

  很多時候我們由於產品模塊的划分,一個頁面可能需要加載幾個腳本,我們需要考慮兩點:1、腳本之間是否有依賴關系,如果存在依賴關系即使我們使用script標簽是按照順序的,但是並行下載是一起下載的,如果出現后面的包先下載完,那么執行腳本時就可能出現錯誤;2、考慮到效率,一般情況下異步加載比同步加載會快一些。為了解決如上問題,我們可以利用之前討論的知識進行組合

<html>
<head>
    <title></title>
    <script type="text/javascript">
        function init()
        {
            //這里第一個參數是一個數組,可以任意多個,加載順序按照數組的順序進行保證
            //第二個參數是回調函數,當所有包都確認加載完畢后需要執行的腳本
            //第三個參數是script的標簽,這個參數可以省略,沒有實質意義
            attachScript(["http://www.cnblogs.com/5/loadJS.js","http://www.cnblogs.com/5/package.js"],operation,"yy")();
        }
        function operation()
        {
            //可以運行,顯示“成功加載”
            functionOne();
        }
        //異步批量加載腳本,並且根據數組urlArray中的url順序來加載
        function attachScript(urlArray, callback, id) {
            if(urlArray && ((typeof urlArray) == "object"))
            {
                if(urlArray.constructor == Array)
                {
                    if(urlArray.length>1)
                    {
                        var array = urlArray.splice(0,1);
                        return function(){
                            var dataScript = document.createElement('script');
                            dataScript.type = 'text/javascript';
                            if(dataScript.readyState) { //IE
                                dataScript.onreadystatechange = function() {
                                    if(dataScript.readyState == 'complete'|| dataScript.readyState == 'loaded'){
                                        dataScript.onreadystatechange = null;
                                        attachScript(urlArray,callback,id)();
                                    }
                                }
                            } else { //standers
                                dataScript.onload = function() {
                                    attachScript(urlArray,callback,id)();
                                }
                            }
                            dataScript.src = array[0];
                            dataScript.id = id;
                            document.body.appendChild(dataScript);
                        }
                    }
                    else if(urlArray.length == 1)
                    {
                        return function(){
                            var dataScript = document.createElement('script');
                            dataScript.type = 'text/javascript';
                            if(dataScript.readyState) { //IE
                                dataScript.onreadystatechange = function() {
                                    if(dataScript.readyState == 'complete'|| dataScript.readyState == 'loaded'){
                                        dataScript.onreadystatechange = null;
                                        callback();
                                    }
                                }
                            } else { //standers
                                dataScript.onload = function() {
                                    callback();
                                }
                            }
                            dataScript.src = urlArray[0];
                            dataScript.id = id;
                            document.body.appendChild(dataScript);
                        }
                    }
                }
            }
        }

    </script>
</head>
<body>
<input type="button" value="測試按鈕" onclick="init()"/>
</body>
</html>

  使用很方便,通過一個方法attachScript可以加載你的任意多個有序的ja包,並且下載的時候還是並行下載,效率上也還不錯,你可以把這個方法單獨打包,方便以后使用,不過里面的代碼也許需要稍微修改一下,有些地方不嚴謹哦!  

  6.2同步分類(或模塊)動態加載

  這里的動態加載是指當用戶使用到了某個類或者模塊才去加載,並且加載不是用戶來控制,而是自動的。

  優點:

    1、同步加載可以很好的保證腳本的依賴關系

    2、用時才加載,可以保證基礎包盡量小,提高用戶體驗

  缺點:

    1、同步加載相對異步加載來說一般偏慢

    2、在未發布的情況下不支持Chrome、Opera

  詳細的說明請看:js動態加載腳本之實用小技巧

  6.3異步分類(或模塊)動態加載

  這里采用回調函數形式的異步加載

  優點:

    1、異步加載速度快

    2、使用回調函數也可以保證腳本的良好依賴關系

    3、同樣時基礎包小,提高用戶體驗

  缺點:

    1、調試不太方便

    2、類被划分為兩個部分,划分難度大

   說明:大家如果了解了6.2的同步分模塊加載后發現最大的缺點在於未發布的情況下不支持某些瀏覽器,並且代碼的執行中途會強制被阻止,當某些代碼下載下來后在執行,這樣的話所有代碼的下載都是呈線性的下載,沒有並行,效率會比較低。

  不知道大家有木有注意過百度地圖的包和google地圖的包是如何實現的,他們實現方式一樣:基礎包都是大概70到80kb的樣子,比較小(所有地圖功能包加起來有200到300左右),第一次訪問地圖的時候就比較小,效率快,用戶體驗好,但是用戶在地圖上面觸發了其他復雜的功能(比如交通換乘)時,你會發現它開始下載一些比較小的包,但這些包是並行下載的。它是如何做到的呢?

  其實基礎包里面已經將所有的API都已經定義了,如果沒定義,那運行到那一塊的時候瀏覽器肯定會報錯誤,但是所有功能都在基礎包里不可能那么小,是因為百度的基礎包里面定義的所有的接口(API),但是只有簡單的屬性是真實可用的,而那些復雜的方法都是空的,也就是這些方法里面只是負責記錄是否執行了此方法,那樣這些方法必定代碼很少,所以基礎包就很小,等程序執行一遍后再去下載需要的功能模塊,下完后再按照之前記錄的信息重新執行一遍,而這些包里面的方法必將覆蓋以前的假的方法,當第二次使用的時候就執行的真正的方法了。

  下面我們來寫一個比較簡單的例子看一下:

  先看一下測試的頁面:  

<html>
<head>
    <title></title>
    <script type="text/javascript" src="Core.js"></script>
    <script type="text/javascript">
        var button;
        function init1()
        {
            button =new SuperMap.Control.Button(20,10);
            var height = button.getHeight();
            button.draw();
        }
        function init2()
        {
            button.draw();
        }
    </script>
</head>
<body>
<input type="button" onclick="init1()" value="按鈕" />
<input type="button" onclick="init2()" value="按鈕2" />
</body>
</html>

  這里點擊“按鈕”,初始化了一個Button的對象,然后調用了方法getHeight(真的簡單方法)和draw(假的復雜方法,第一次調用,只是記錄),再次點擊“按鈕2”,調用方法draw(真的復雜方法,第二次調用,假的已經被覆蓋了)。

  這里Core.js是基礎包,代碼如下: 

//一下為框架代碼,大家不需要了解
window.SuperMap = {

    VERSION_NUMBER: "Release 6.1.3",

    _getScriptLocation: (function() {
        //SuperMap-6.1.1-8828
        var r = new RegExp("(^|(.*?\\/))(SuperMap(-(\\d{1}\.)*\\d{1}-\\d{4,})?\.js)(\\?|$)"),
            s = document.getElementsByTagName('script'),
            src, m, l = "";
        for(var i=0, len=s.length; i<len; i++) {
            src = s[i].getAttribute('src');
            if(src) {
                var m = src.match(r);
                if(m) {
                    l = m[1];
                    break;
                }
            }
        }
        return (function() { return l; });
    })()
};

SuperMap.Control = SuperMap.Control || {};

SuperMap.Util = SuperMap.Util || {};

SuperMap.Class = function() {
    var len = arguments.length;
    var P = arguments[0];
    var F = arguments[len-1];

    var C = typeof F.initialize == "function" ?  F.initialize : function(){ P.prototype.initialize.apply(this, arguments); };

    if (len > 1) {
        var newArgs = [C, P].concat( Array.prototype.slice.call(arguments).slice(1, len-1), F);
        SuperMap.inherit.apply(null, newArgs);
    } else {
        C.prototype = F;
    }
    return C;
};
SuperMap.inherit = function(C, P) {
    var F = function() {};
    F.prototype = P.prototype;
    C.prototype = new F;
    var i, l, o;
    for(i=2, l=arguments.length; i<l; i++) {
        o = arguments[i];
        if(typeof o === "function") {
            o = o.prototype;
        }
        SuperMap.Util.extend(C.prototype, o);
    }
};

SuperMap.Util = SuperMap.Util || {};
SuperMap.Util.extend = function(destination, source) {
    destination = destination || {};
    if (source) {
        for (var property in source) {
            var value = source[property];
            if (value !== undefined) {
                destination[property] = value;
            }
        }
        var sourceIsEvt = typeof window.Event == "function"
            && source instanceof window.Event;

        if (!sourceIsEvt
            && source.hasOwnProperty && source.hasOwnProperty("toString")) {
            destination.toString = source.toString;
        }
    }
    return destination;
};
SuperMap.Util.copy = function(des, soc) {
    des = des || {};
    var v;
    if(soc) {
        for(var p in des) {
            v = soc[p];
            if(typeof v !== 'undefined') {
                des[p] = v;
            }
        }
    }
};
SuperMap.Util.reset = function(obj) {
    obj = obj || {};
    for(var p in obj) {
        if(obj.hasOwnProperty(p)) {
            if(typeof obj[p] === "object" && obj[p] instanceof Array) {
                for(var i in obj[p]) {
                    if(obj[p][i].destroy) {
                        obj[p][i].destroy();
                    }
                }
                obj[p].length = 0;
            } else if(typeof obj[p] === "object" && obj[p] instanceof Object) {
                if(obj[p].destroy) {
                    obj[p].destroy();
                }
            }
            obj[p] = null;
        }
    }
};
//以下為核心代碼
//加載腳本的方法
SuperMap.Util.loadJs = function(urlArray, callback, id) {
    if(urlArray && ((typeof urlArray) == "object"))
    {
        if(urlArray.constructor == Array)
        {
            if(urlArray.length>1)
            {
                var array = urlArray.splice(0,1);
                return function(){
                    var dataScript = document.createElement('script');
                    dataScript.type = 'text/javascript';
                    if(dataScript.readyState) { //IE
                        dataScript.onreadystatechange = function() {
                            if(dataScript.readyState == 'complete'|| dataScript.readyState == 'loaded'){
                                dataScript.onreadystatechange = null;
                                SuperMap.Util.loadJs(urlArray,callback,id)();
                            }
                        }
                    } else { //standers
                        dataScript.onload = function() {
                            SuperMap.Util.loadJs(urlArray,callback,id)();
                        }
                    }
                    dataScript.src = array[0];
                    dataScript.id = id;
                    document.body.appendChild(dataScript);
                }
            }
            else if(urlArray.length == 1)
            {
                return function(){
                    var dataScript = document.createElement('script');
                    dataScript.type = 'text/javascript';
                    if(dataScript.readyState) { //IE
                        dataScript.onreadystatechange = function() {
                            if(dataScript.readyState == 'complete'|| dataScript.readyState == 'loaded'){
                                dataScript.onreadystatechange = null;
                                callback();
                            }
                        }
                    } else { //standers
                        dataScript.onload = function() {
                            callback();
                        }
                    }
                    dataScript.src = urlArray[0];
                    dataScript.id = id;
                    document.body.appendChild(dataScript);
                }
            }
        }
    }
}
//用於記錄模塊是否加載
SuperMap.Util.IsControl = false;
//按照模塊名稱來加載腳本
SuperMap.Util.load = function(backName,callbackFunction){
    if(backName == "Control")
    {
        if(SuperMap.Util.IsControl == false)
        {
            SuperMap.Util.loadJs(["Control.js"],callbackFunction,546756)();
        }
        SuperMap.Util.IsControl == true;
    }
    //其他模塊
    else if(...)
    {
      ...
    }
    //....
}
//用於測試的類 Control模塊
SuperMap.Control.Button = SuperMap.Class({
    w: 0.0,
    h: 0.0,
    initialize: function(w, h) {
        this.w = parseFloat(w);
        this.h = parseFloat(h);
        this.flow = [];
        //需要注冊一個回調函數
        var c = this;
        SuperMap.Util.load("Control",function(){
            //等加載完腳本后重新執行一遍
            c.init();
        });
    },
    getWidth:function()
    {
        return this.w;
    },
    setWidth:function(value)
    {
        this.w = value;
    },
    getHeight:function()
    {
        return this.h;
    },
    setHeight:function(value)
    {
        this.h = value;
    },
    getArea:function() {

        return this.w * this.h;
    },
    clone:function() {
        return new SuperMap.Control.Button(this.w, this.h);
    },
    //假的方法
    draw:function(){
        this.flow.push({method: "draw", arguments: null});
    },
    CLASS_NAME: "SuperMap.Control.Button"
});

  這里的SuperMap.Control.Button類為不完整的類,注意構造函數里面有一個回調函數,當整個類加載完后通過init入口重新執行一遍操作,而里面除了draw是假的以外,其他都是真的。

  再來看一下Control.js模塊:

var Button = SuperMap.Control.Button;
//必須有的入口方法
Button.prototype.init = function()
{
    for(var i = 0;i<this.flow.length;i++)
    {
        //挨個執行
        this[this.flow[i].method](this.flow[i].arguments);
    }
    delete this.flow;
}
//比較復雜的方法
Button.prototype.draw = function()
{
    //....
    //alert("繪制完畢");
}

  看完思路大家就會發現他有自己的缺點:我們調試怎么辦,調試的時候獲取的對象就不是那么明顯了,很不方便;怎么把一個類划分成為兩個部分,這一點特別的難,我不是百度的員工,也不清楚他們的標准。不過可以猜想他們這樣的有點很明顯:你管我代碼怎么的,反正用戶用起來發現效率很快就行,基礎包很小,用到哪塊再加載哪塊,用戶體驗特別的好,並行加載,效率快。

  沒有什么是100%滿意的,只要抓住重點就行,百度和google舍棄了調試的便利以及開發的簡易(使程序員痛苦),但是獲得了廣大用戶的良好評價,這就是他們的目的。

  這里的三點實用方式希望能給大家給予一定的幫助吧!

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM