《高性能JavaScript》讀書筆記①加載和執(zhí)行

JavaScript的加載和執(zhí)行(Loading and Execution)

JavaScript的阻塞特性

  • 當瀏覽器遇到JavaScript代碼段時(不管是內(nèi)嵌還是外鏈),由于不確定其是否會進行DOM操作,所以都會等待腳本的解析和執(zhí)行完成后,再往下運行。此時頁面渲染和用戶交互都是被阻塞的,這就是 JavaScript的阻塞特性 。

JavaScript阻塞特性的思考

  • 雅虎性能小組對JS代碼優(yōu)化的首要規(guī)則就是,通過改變<script>腳本的位置來減少對頁面加載的影響。也就是說將<script>元素放到<body>標簽底部,這樣頁面的CSS和圖片等資源文件加載時,就不需要等待JS代碼的解析和執(zhí)行了。
  • 除此之外,減少<script>標簽的數(shù)量也能改善阻塞的情況
    1. 我們可以合并<script>代碼段,因為瀏覽器在解析HTML頁面時,每遇到一個<script>標簽,都會因為執(zhí)行腳本而導(dǎo)致一定的延時。
    2. 我們還需要盡可能的合并外鏈的JavaScript文件。測試發(fā)現(xiàn),下載一個100KB的JS文件比下載四個25KB的JS文件要快,是因為每次HTTP請求相當于重新進行一次Socket連接,會產(chǎn)生性能損耗。

實現(xiàn)Noblocking Scripts(無阻塞的腳本)的幾種方式

  • Deferred Scripts (腳本延遲)
    針對JavaScript的阻塞特性,很多人首先想到的辦法可能是:能不能先下載JS文件,等頁面加載完成后,再去解析和執(zhí)行引入的JavaScript代碼呢?
    針對此種設(shè)想,HTML4標準為<script>標簽提供了defer屬性。意味著可以先下載JS文件(同時允許并行下載),等到DOM加載完成(即onload事件被觸發(fā)前),才會執(zhí)行其中的代碼段。

    <script type="text/javascript" src="file1.js" defer/>
    

    注意:該屬性只對聲明了src屬性的<script>標簽生效,另外HTML5規(guī)范中引入了async屬性,和defer屬性一樣,也是用于異步加載腳本的

  • Dynamic Script Elements (動態(tài)腳本元素)
    我們知道DOM(文檔對象模型)機制使得我們可以動態(tài)的創(chuàng)建、編輯、移動和刪除HTML元素,這里面當然也包含<script>元素。也就是說,其實我們是可以通過JavaScript代碼來動態(tài)構(gòu)建<script>標簽的。

    //創(chuàng)建script元素
    var script = document.creatElement("script"); 
    //指定元素的type屬性
    script.type = "text/javascript"; 
    //指定URL
    script.src = "file1.js"; 
    //將新的script標簽加入到文檔頭部
    document.getElementByTagName("head")[0].appendChild(script);     
    

    注意,當新的<script>元素被加入到<head>時,會立即開始下載file1.js文件,并執(zhí)行其中的JS腳本。這種方式的優(yōu)勢就在于,能夠把<script>腳本放在頁面<head>區(qū)域,避免影頁面其他部分的加載。
    如果需要監(jiān)聽腳本的下載狀態(tài),可以通過onload()方法監(jiān)聽腳本加載完成的狀態(tài):

    var script = document.creatElement("script");
    script.type = "text/javascript";
    //js下載完畢后的監(jiān)聽(IE中不可用)
    script.onload = function(){
        alert("script loaded!");
    };
    script.src = "file1.js";
    document.getElementByTagName("head")[0].appendChild(script);    
    
    

    但是onload()方法僅在Firefox、Chrome、Opera和Safari3+ 瀏覽器中支持,如果想在IE瀏覽器要監(jiān)聽js腳本的下載狀態(tài),則可以通過監(jiān)聽<script>元素的readyState狀態(tài)屬性來實現(xiàn)。
    readyState有五種取值,但是微軟的相關(guān)文檔表明,在整個<script>元素生命周期中,并非readyState的每個值都會用到。最終表明<script>元素加載完畢的是"loaded"和"complete"兩個狀態(tài),所以我們要檢查這兩個狀態(tài)。

    var script = document.creatElement("script");
    script.type = "text/javascript";
    //js下載完畢后的監(jiān)聽(IE)
    script.onreadystatechange = function(){
        if(script.readyState == "loaded" || script.readyState == "complete"){
            alert("script loaded!");
        }
    }
    

    由此可見通過動態(tài)腳本元素的方式解決JavaScript的阻塞問題時,如果需監(jiān)聽下載狀態(tài),還需要針對不同瀏覽器做封裝處理。

    //定義公共的js加載方法(有沒有覺得這種callback的方式和$.Ajax很類似?)
    function loadScript(url,callback){
        var script = document.creatElement("script");
        script.type = "text/javascript";
        //判斷如果能獲取readyState屬性,則表明是IE瀏覽器
        if(script.readyState){
         script.onreadystatechange = function(){
             if(script.readyState == "loaded" || script.readyState == "complete"){
             script.onreadysatechange = null;
             callback();
         }
         }
        //其他瀏覽器
        }else{
         script.onload = function(){
         callback();
         }
        }
        script.src = url;
        document.getElementByTagName("head")[0].appendChild(script);
    }
    
    //調(diào)用
    loadScript("file1.js",function(){
     alert("script loaded!");
    });
    
    
  • XMLHttpRequest Script Injection (XMLHttpRequest腳本注入)
    另一種無阻賽加載腳本的方式,是使用XMLHttpRequest(XHR)對象加載JS腳本
    由于XMLHttpRequest腳本注入的方式,由JS腳本的加載以及<script>元素的創(chuàng)建兩部分組成,我們就可更加靈活的控制JavaScript腳本,比如先加載,等到需要時候再執(zhí)行。

//創(chuàng)建XMLHttpRequest對象
var xhr = new XMLHttpRequest();
//通過GET請求加載file1.js
xhr.open("get","file1.js",true);
//通過XHR對象的onreadystatechange()方法監(jiān)聽readState屬性的變化
xhr.onreadystatechange = function(){
//readyState屬性表示XHR對象請求的就緒狀態(tài):0-請求未初始化;1-請求已建立;2-請求已發(fā)送;3-請求處理中;4-響應(yīng)已完成
if(xhr.readyState == 4){
//status屬性表示HTTP請求的狀態(tài)碼:200-(OK)請求成功;304-(Not Modified)從緩存中讀??;狀態(tài)碼小于300都表示請求成功
if(xhr.status >= 200 && (xhr.status < 300 || xhr.status == 304)){
//創(chuàng)建script元素
var script = document.creatElement("script");
//指定元素的type屬性
script.type = "text/javascript";
//將response響應(yīng)內(nèi)容填充到script元素中
script.text = xhr.responseText;
//當把script元素添加到body時,js代碼會被立即執(zhí)行
document.body.appendChild(script);
}
}
}
```

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容