JSON傳輸格式,瀏覽器渲染頁面過程,JS異步加載,js時間線

JSON傳輸格式

json是一種傳輸數據的格式(以對象爲樣板,本質上即使對象,但是用途有區別,對象就是本地用的,json是用來傳輸的)

對象的屬性加不加雙引號都可以,但是json規定 json格式的屬性名必須加上雙引號

JSON是一個靜態類(類似於Math),不用去構造它,它自己就有這些方法。

  • JSON.stringifg(); json—>string

  • JSON.parse(); string—>json

 var obj = {
	   "name" : "abc",
       "age"  : 123
  }
var str = JSON.stringify(obj);
console.log(str);//"{"name":"abc","age":123}"

console.log(JSON.parse(str));//Object{name: "abc", age: 123}

前後端傳輸的時候傳的都是json格式的字符串

瀏覽器渲染頁面過程

第一步,首先是建立一個dom樹,它是解析所有的dom節點(深度遍歷的方法)
解析dom節點就是解析到哪個dom節點就把它掛到dom樹上
像碰到< img src = “XXX”/>這樣的還是隻把img節點掛到dom樹上,意思就是說dom樹的構建完成就是所有dom節點的解析完畢,並不代表所有dom節點加載完畢(dom的解析完畢一定在dom加載完畢之前)。
在這裏插入圖片描述

第二步生成cssTree
瀏覽器解析CSS代碼,計算出最終的樣式數據。解析CSS的時候會按照如下順序來定義優先級:瀏覽器默認設置,用戶設置,外聯樣式,內聯樣式,html中的style(嵌在標籤中的行間樣式)。
在這裏插入圖片描述

第三步生成randertree,也可以說是渲染樹
渲染樹中每一個節點都存儲着對應的CSS屬性。
因爲js可以操作dom,所以當渲染樹建立後,如果動態的操作了dom,機會導致頁面重新渲染,叫做reflow重排(重構)

reflow重排:dom節點的刪除,添加。dom節點的寬高變化,位置變化,displaynone–>block,offsetwidth,offsetLeft等一系列都會導致重排。

repaint重繪:js改變了節點 的css,會導致那一部分的重繪,這樣影響比較小。

在這裏插入圖片描述

第四步:當渲染樹創建完成之後,瀏覽器就可以根據渲染樹直接把頁面繪製到屏幕上。
DOM樹完全和html標籤一一對應,而渲染樹會忽略不需要渲染的元素(head、display:none的元素)。

JS異步加載

js加載的缺點

js加載本身是屬於同步加載的,加載js文件會阻塞文檔,比如有多個js文件,在加載到一個文件處有錯後面的就加載不了

或者一旦網速不好,那麼整個網站將等待js加載而不進行後續渲染等工作

但是有些工具方法需要按需加載,有一些工具js文件它是不會改變頁面的,用到再加載,不用不加載。

javascript異步加載的三種的方案

  1. . defer異步加載

defer異步加載,但要等到dom文檔全部解析完纔會被執行,也可以將代碼寫到內部。
defer 屬性是一個布爾屬性。

<script src="demo_defer.js" defer></script> 
  1. async 異步加載

async 異步加載,加載完就執行,async 只能加載外部腳本(只有在使用 src 屬性時),不能把js寫在script標籤裏。
Internet Explorer 9 及之前的版本不支持 script 標籤的 async 屬性。
異步腳本一定會在頁面 load 事件前執行。

<script src="demo_async.js"  async></script>

defer和async異步加載時不阻塞頁面,但是兼容性不好,不能控制加載的順序

3. 創建script,插入到DOM中,加載完畢後callBack

<script>
       var script = document.createElement('script');
       script.type = "text/javascript";
       script.src = "demo.js";//到了這一步之後就會異步的去下載demo.js文件
       document.head.appendChild(script);//當把標籤插入到頁面的時候纔會去執行這個js腳本
 </script>

接下來我麼創建一個名爲demo.js文件寫入test函數

function test(){
condole.log("a“)}

執行他

<script>
       var script = document.createElement('script');
       script.type = "text/javascript";
       script.src = "demo.js";//到了這一步之後就會異步的去下載demo.js文件

       document.head.appendChild(script);//當把標籤插入到頁面的時候纔會去執行這個js腳本
       test();
  
    </script>

執行到test()時候會報錯
Uncaught ReferenceError: test is not defined

這是因爲文件還沒有下載完,讀程序是以微秒計的。
有可能異步的下載demo.js文件還沒有下載完,程序就讀到test執行這了
還沒有下載完demo.js,當然沒有test函數了

接着我們 添加一個setTimeout() 方法,讓test()在1000毫秒後再執行時不時就不報錯了

<script>
       var script = document.createElement('script');
       script.type = "text/javascript";
       script.src = "demo.js";//到了這一步之後就會異步的去下載demo.js文件

       document.head.appendChild(script);//當把標籤插入到頁面的時候纔會去執行這個js腳本
       setTimeout(function () {
            test();
        }, 1000)
  </script>     

但是這樣並不能從根本上解決問題,我們需要知道程序什麼時候異步把外部js文件下載完成

下面介紹幾種方法讓我們知道什麼時候加載完成

  • load事件監聽

Safari,chrome,firefox,opera都可以用這個來監聽什麼時候這個文件異步下載完畢

<script>
       var script = document.createElement('script');
       script.type = "text/javascript";
       script.src = "demo.js";
       script.onload = function(){
             test();
       }
       document.head.appendChild(script);
    </script>

當文件下載完之後就會執行onload方法,這樣就知道什麼時候demo.js加載完了。

  • 如果是IE瀏覽器 就用·狀態碼的改變·來知道什麼時候完成文件的下載
<script>
       var script = document.createElement('script');
       script.type = "text/javascript";
       script.src = "demo.js";
	   script.onreadystatechange = function(){
           if(script.readyState == "complete" || script.readyState == "loaded"){
               test();
           }
       }
	   document.head.appendChild(script);
    </script>

綜合以上兩鍾方法實現所有瀏覽器兼容

<script>
       var script = document.createElement('script');
       script.type = "text/javascript";
       script.src = "demo.js";//到了這一步之後就會異步的去下載demo.js文件
       
       if(script.readyState){
            script.onreadystatechange = function(){
            if(script.readyState == "complete" || script.readyState == "loaded"){
                test();
            }
         }
       }else{
            script.onload = function(){
                test();
            }
       }
   
       document.head.appendChild(script);
      
    </script>

案例:封裝一個函數兼容性的異步加載js文件並且可以按需執行該文件裏面的函數

callback:回調函數
在事件裏面的綁定的事件處理函數就是回調函數,回調函數其實就是當滿足一定條件才調用的函數。

      function loadScript (url, callback){
           // url是異步下載的js文件
           //callback是異步下載的js文件中的某一個函數
        var script = document.createElement('script');
        script.type = "text/javascript";
        script.src = url;//異步下載的js文件
        if(script.readyState){
            script.onreadystatechange = function(){
                if(script.readyState == "complete" || script.readyState == "loaded"){
                    callback();
                }
           }
        }else{
            script.onload = function(){
                callback();
            }
        }
        
        document.head.appendChild(script);
       }
</script>

在IE上如果下載太快(比讀程序還快),IE的readystatechange 事件檢測狀態碼的時候,它早已經從loading變成complete或者loaded(以極快的速度加載完了文件,你還沒來得及檢測),
那你再檢測它就不會變了,它一直都是complete或者loaded,這個時候檢測也沒用了。

<script>
function loadScript (url, callback){
           // url是異步下載的js文件
           //callback是異步下載的js文件中的某一個函數
        var script = document.createElement('script');
        script.type = "text/javascript";
  
        if(script.readyState){
            script.onreadystatechange = function(){
                if(script.readyState == "complete" || script.readyState == "loaded"){
                    callback();
                }
           }
        }else{
            script.onload = function(){
                callback();
            }
        }
        script.src = url;//開始異步下載的js文件
        document.head.appendChild(script);
       }
</script>

先綁定事件,然後再script.src = url開始異步下載js文件,那麼這個時候下載,肯定有一個狀態碼的轉換,這樣就解決了來不及檢測狀態碼的尷尬。

時間線

  1. 創建Documen對象開始解析web頁面。解析HTML元素和他們的文本內容後添加Element對象和Text節點到文檔中。這個階段document.readyState= ‘loading’ 。

  2. 遇到link外部css,創建線程加載,井繼續解析文檔。

  3. 遇到script外部js,並且沒有設置async,defer, 瀏覽器加載,並阻塞,等待js加載完成並執行該腳本,然後繼續解析文檔。

  4. 遇到script外部js.並且設置有async, defer,瀏覽器創建線程加載,並繼續解析文檔。對於async屬性的腳本,腳本加載完成後立即執行。(異步禁止使用document.write(),因爲它會清除文檔流)

  5. 遇到img等,先正常解析dom結構,然後瀏覽器異步加載src,並繼續解析文檔。

  6. 當文檔解析完成,document.readyState = ‘Interactive’。

  7. 文檔解析完成後,所有設置有defer的腳本會按照順序執行。(注意 async的不同,但同樣禁止使用document.wite()) ;

  8. document對象觸發DOMContentLoaded事件,這也標誌着程序執行從同步腳本執行階段,轉化爲事件驅動階段。

  9. 當所有async的腳本加載完成井執行後。img等加載完成後,document.readyState = ‘complete’, window對象觸發load事件。

  10. 從此,以異步響應方式處理用戶輸入,網絡事件等。

document.write()會清除文檔流

<body>
    <div style="width:100px;height:100px;background-color:red;"></div>
    <script>
        window.onload = function () {
            document.write('a');
        }
    </script>
</body>

這個時候頁面就只會輸出a,div就被清除了

創建Documen對象開始解析web頁面,script標籤也是dom元素,所以它也要識別,這個階段document.readyState= ‘loading’ 。

<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
 <script>
        console.log(document.readyState);//loading  
<script>
</body>
</html>

用狀態碼改變才能檢測文檔解析到哪個狀態了

<body>
<script>
      console.log(document.readyState);//loading  
       document.onreadystatechange = function (){
          console.log(document.readyState);// interactive   complete
      }
<script>
</body>

DOMContentLoaded事件只有用addEventListener綁定纔有效

document.addEventListener('DOMContentLoaded',function(){ console.log('a')},false);

正常的情況下,當文檔加載(解析文檔,就是構成dom樹,然後再加載dom元素)完畢後,才執行js代碼,
但是document對象觸發DOMContentLoaded事件可以當dom解析完了(dom樹構建完成,還沒加載完畢的時候)就去執行js代碼

/就像jquery裏面的
當dom解析完就執行的部分
$(document).ready(function (){

})

現在用document對象觸發DOMContentLoaded事件放在head裏面,照樣可以拿到div
因爲它是在dom樹構建完成之後就調用事件的那個處理函數,不用等到文檔全部加載完畢
而window.onload()就是要等到文檔全部加載完畢了(像img的src的圖片加載完成了),它纔會執行window.onload()裏面的js。

<!DOCTYPE html>
<html lang="en">
<head>
    <script>
        document.addEventListener('DOMContentLoaded',function(){
            var div = document.getElementsByTagName('div')[0];
            console.log(div);
        },false);
    </script>
</head>
<body>
    <div style="width:100px;height:100px;background-color:red"></div>
</body>
</html>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章