• <sup id="mk476"></sup>
    <dl id="mk476"></dl>
  • <progress id="mk476"><tr id="mk476"></tr></progress>
    <div id="mk476"><tr id="mk476"></tr></div>
    <sup id="mk476"><ins id="mk476"></ins></sup>
  • <progress id="mk476"></progress>
    <div id="mk476"></div>
    <div id="mk476"><tr id="mk476"></tr></div>
  • <div id="mk476"></div>
    <dl id="mk476"><s id="mk476"></s></dl><dl id="mk476"></dl><div id="mk476"></div>
  • <div id="mk476"></div>
    <dl id="mk476"><ins id="mk476"></ins></dl>

    高性能JavaScript之加載和執行

    JS在瀏覽器中的性能,可以認為是開發者所面臨的最重要的可行性問題。這個問題因JS的阻塞特性變得復雜,也就是說當瀏覽器在執行JS代碼時,不能同時做其他任何事情。事實上,大多數瀏覽器都使用單一進程來處理UI(用戶界面)更新和JavaScript腳本執行,所以同一時刻只能做其中一件事情。JS執行過程耗時越久,瀏覽器等待響應用戶輸入的時間就越長。

     

    從基礎層面來說,這意味著<script>標簽每次出現都霸道地讓頁面等待腳本的解析和執行。無論當前的JS代碼是內嵌的還是在外鏈文件中,頁面的下載和渲染都必須停下來等待腳本的執行完成。這在頁面生存周期中是必要的,因為腳本執行過程中可能會修改頁面的內容。一個典型的例子就是在頁面中使用document.write()(經常用來顯示廣告)。

    例如:

    <html>
        
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
            <title>Script Example</title>
           
        </head>
    
        <body>
          <p>
          <script>
          document.write("The date is "+(new Date()).toDateString());
          </script>
          </p>
        </body>
    </html>

     

    當瀏覽器遇到<script>標簽時,當前的HTML頁面無從獲知JS是否會向<p>標簽添加內容,或引入其他元素,或關閉該標簽。因此,這時瀏覽器會停滯處理頁面,先執行JS代碼,然后再繼續解析和渲染頁面。同樣的情況也發生在使用src的屬性加載JS的過程中,瀏覽器必須先花時間下載外鏈文件中的代碼,然后解析并執行它。在這個過程中,頁面渲染和用戶交互完全被阻塞了。

     

    1.1腳本位置

    這里先說說HTML4規范,HTML4規范指出<script>標簽可以放在HTML文檔的<head>或<body>中,并允許出現多次。按照慣例,<script>標簽用來加載出現在<head>中的外鏈JS文件中,挨著的<link>標簽用來加載外部CSS文件或其他頁面元信息。也就是說,把與樣式和行為有關的腳本放在一起,并先加載它們,使得頁面能夠顯示正確的外觀和交互。

    例如:

    <html>
        
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
            <title>Script Example</title>
            <script src="file1.js"></script>
            <script src="file2.js"></script>
            <script src="file3.js"></script>
            <link rel="stylesheet" type="text/css" href="style.css">
           
        </head>
    
        <body>
          <p>
    Hello World
          </p>
        </body>
    </html>

     

    這些看似正常的代碼實際上有十分嚴重的性能問題:在<head>中加載了三個JS文件。由于腳本會阻塞頁面的渲染,直到它們全部下載并執行完成后,頁面的渲染才會繼續。

    因此頁面的性能問題會很明顯。請記住,瀏覽器在解析到<body>標簽之前,不會渲染頁面的任何部分。把腳本放到頁面頂部將會導致明顯的延遲,通常表現為顯示空白頁面,用戶無法瀏覽內容,也無法與頁面進行交互。

    所以通常建議像JS腳本一般都放在</body>前,也就是頁面最底下,而CSS文件放在<head></head>之間。雖然說CSS文件過大也會導致延遲,但是這種延遲是可以接受的,如果是JS腳本與CSS腳本放在<head></head>之間如上面的代碼所示,那樣的話,延遲會顯得十分明顯。因此推薦<script>標簽盡可能放到<body>標簽底部,</body>標簽之前,以盡量減少對整個頁面下載的影響。

    例如:

    <html>
        
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
            <title>Script Example</title>
            <link rel="stylesheet" type="text/css" href="style.css">
           
        </head>
    
        <body>
          <p>
    Hello World
          </p>
    
            <script src="file1.js"></script>
            <script src="file2.js"></script>
            <script src="file3.js"></script>
        </body>
    </html>

     

    記得在《高性能網站建設》這本書,其中提到的建議之一:就是將腳本放在底部。

     

    1.2組織腳本

    由于每個<script>標簽初始下載時,都會阻塞頁面渲染,所以減少頁面包含的<script>標簽數量有助于改善這一情況。這不僅僅針對外鏈腳本,內嵌腳本的數量同樣也要限制。瀏覽器在解析HTML頁面的過程中每遇到一個<script>標簽,都會因執行腳本而導致一定的延時,因此最小延遲時間將會明顯改善頁面的總體性能。

     

    一般情況下,組織腳本不單單是將JS文件中的注釋或者其他無關緊要的內容去掉,而且也要將其壓縮,通過YUI或者是將多個JS文件合并壓縮成一個大的JS文件。只需引用一個<script>標簽,就可以減少性能的損耗(主要是減少了因加載多個腳本導致的延時)。多個合并壓縮成一個大的JS文件,并將其放在CDN中并引入也是可以的。

     

    1.3無阻塞腳本

    JS傾向于阻止瀏覽器的某些處理過程,如HTTP請求和用戶界面更新,這是開發者所面臨的最顯著的性能問題。減少JS文件大小并限制HTTP請求數僅僅是創建響應迅速的Web應用的第一步。Web應用的功能越豐富,所需要的JS代碼就越多,所以精簡源代碼不總是可行的。盡管下載單個較大的JS文件只產生一個HTTP請求,卻會鎖死瀏覽器一大段時間。為了避免這種情況,你需要向頁面中逐步加載JS文件,這樣做在某種程度上來說不會阻塞瀏覽器。

    無阻塞腳本的秘訣在于,在頁面加載完成后才加載JS代碼。用專業術語來說,這意味著在window對象中的load事件觸發后再下載腳本。有多種方式可以實現這一效果。

     

    1.3.1延遲腳本

    HTML4為<script>標簽定義了一個擴展屬性:defer。Defer屬性指明本元素所含的腳本不會修改DOM,因此代碼能安全地延遲執行。該屬性只有IE4和FireFox3.5+的瀏覽器支持,所以它不是一個理性的跨瀏覽器解決方案。在其他瀏覽器中,defer屬性會被直接忽略,因此<script>標簽會以默認的方式處理(即會造成阻塞)。然而,如果你的目標瀏覽器支持的話,這仍然是個有用的解決方案。

    帶有defer屬性的<script>標簽可以放置在文檔的任何位置。對應JS文件將頁面解析到<script>標簽時開始下載,但并不會執行,直到DOM加載完成(onload事件被觸發前)。當一個帶有defer屬性的JS文件下載時,它不會阻塞瀏覽器的其他進程,因此這類文件可以與頁面中的其他資源并行下載。

    任何帶有defer屬性的<script>元素在DOM完成加載之前都不會被執行,無論內嵌或外鏈腳本都是如此。

    例如:

     

    <html>
        
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
            <title>Script Example</title>
        
           
        </head>
    
        <body>
      <script defer>
        alert("defer");
     </script>
     
     <script>
     alert("script");
     </script>
     
     
     <script>
     window.onload=function(){
        alert("load");
     }
     </script>
        </body>
    </html>

     

    這段代碼在頁面處理過程中會彈出三次提示框。不支持defer屬性的瀏覽器的彈出屬性是"defer"、"script"、"load"。而在支持defer屬性的瀏覽器上,彈出的順序是:"script"、"defer"、"load"。請注意,帶有defer屬性的<script>元素不是跟在第二個后面執行,而是在onload事件處理器執行之前被調用。

    當然了,目前我在我自己電腦上執行了上述代碼,基本都不支持defer,可能需要更低的版本才能支持。

     

    1.3.2動態腳本元素

    通過文檔對象模型,你幾乎可以用JS動態創建HTML中的所有內容。其根本在于,<script>標簽與頁面中的其他元素并無差異:都能通過DOM引用,都能在文檔中移動、刪除、甚至被創建。用標準的DOM方法可以非常容易地創建一個新的<script>元素。

     

    13.3XMLHttpRequest腳本注入

    另外一種無阻塞加載的腳本方法是使用XMLHttpRequest對象獲取腳本并注入頁面中。此技術會先創建一個XHR對象,然后用它下載JS文件,最后通過創建動態<script>元素將代碼注入頁面中。

    var xmlhttp;
    if (window.XMLHttpRequest)
      {// code for IE7+, Firefox, Chrome, Opera, Safari
      xmlhttp=new XMLHttpRequest();
      }
    else
      {// code for IE6, IE5
      xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
      }
    xmlhttp.onreadystatechange=function()
      {
      if (xmlhttp.readyState==4 && xmlhttp.status==200)
        {
        document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
        }
      }
    xmlhttp.open("GET","test.js",true);
    xmlhttp.send();
    }

     

    這段代碼發送一個GET請求獲取test.js文件。事件處理函數onReadyStateChange檢查readyState是否為4,同時校驗HTTP狀態碼是否有效(200表示有效響應,304意味著從緩存中讀取)。

    這種方法主要優點是:你可以下載JS代碼但不立即執行。由于代碼是在<script>標簽之外返回的,因此它下載后不會自動執行,這使得你可以把腳本的執行推行到你準備好的時候。另一個優點是,同樣的代碼再所有的主流瀏覽器中無一例外都能正常工作。

    這種方法的主要局限性是JS文件必須與所請求的頁面處于相同的域,這意味著JS文件不能從CDN下載。因此大型Web應用通常不會采用XHR腳本注入。

     

    1.3.4推薦的無阻塞模式

    向頁面中添加大量JS的推薦做法只需兩步:先添加動態加載所需的代碼,然后加載初始化頁面所需的剩下的代碼。因為第一部分的代碼盡量精簡,甚至可能只包含loadScript()函數,它下載執行都很快,所以不會對頁面有太多影響。一旦初始代碼就位,就用它來加載剩余的JS。

    例如:

    <script src="loader.js"></script>
    <script>
    loadScript("the-rest.js",function(){
        Application.init();
    });

     

    把這段代碼加載放在</body>閉合標簽之前。這樣做有幾個好處:

    (1)確保JS執行過程中不會阻礙其他內容顯示;

    (2)當第二個JS文件完成下載時,應用所需的所有DOM結構已經創建完畢,并做好交互準備,從而避免了需要另一個事件(比如window.onload)來檢測頁面是否準備好。

     

    小結:

    管理瀏覽器中的JS代碼是個棘手的問題,因為代碼執行過程中會阻塞瀏覽器的其他進程,比如用戶界面繪制。每次遇到<script>標簽,頁面都必須停下了等待代碼下載(如果是外鏈文件)并執行,然后繼續處理其他部分。盡管如此,還是有幾種方法能減少JS對性能的影響:

    (1)</body>閉合標簽之前,將所有的<script>標簽放到頁面底部。這能確保在腳本執行前,頁面已經完成渲染;

    (2)合并腳本。頁面中的<script>標簽越少,加載也就越快,響應也就越迅速。無論是外鏈還是內嵌腳本都是如此;

    (3)有多種無阻塞下載JS的方法:

      a.使用<script>的defer屬性(注意:高版本瀏覽器不支持);

      b.動態創建<script>元素來下載并執行代碼;

      c.使用XHR對象下載JS代碼并注入頁面中;

    通過以上策略,可以極大的提高那些需要使用大量JS的Web應用的實際性能。

     

    我的感觸:

    全文本質其實這么幾個?

    1.JS腳本放置最底下(避免延遲導致渲染效果差);

    2.合并代碼,將大量JS合并和壓縮為一個JS文件,本質上減少HTTP請求,同時也減少并行下載帶來的延遲;

    做到上述兩點Web應用的性能也會得到很大程度上的提升,特別是做到2,2也正說明了webpack或者gulp流行的重要原因。

    posted @ 2018-09-22 20:17 挑戰者V 閱讀(...) 評論(...) 編輯 收藏
    江苏11选5软件