成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

js文件加載優(yōu)化

zhaochunqi / 2575人閱讀

摘要:所以這里需要另外的操作來(lái)對(duì)文件加載進(jìn)行優(yōu)化加載這是中定義的一個(gè)屬性,它用來(lái)表示的是,當(dāng)渲染引擎遇到的時(shí)候,如果引用的是外部資源,則會(huì)暫時(shí)掛起,并進(jìn)行加載。

在js引擎部分,我們可以了解到,當(dāng)渲染引擎解析到script標(biāo)簽時(shí),會(huì)將控制權(quán)給JS引擎,如果script加載的是外部資源,則需要等待下載完后才能執(zhí)行。 所以,在這里,我們可以對(duì)其進(jìn)行很多優(yōu)化工作。

放置在body底部

為了讓渲染引擎能夠及早的將DOM樹給渲染出來(lái),我們需要將script放在body的底部,讓頁(yè)面盡早脫離白屏的現(xiàn)象,即會(huì)提早觸發(fā)DOMContentLoaded事件. 但是由于在IOS Safari, Android browser以及IOS webview里面即使你把js腳本放到body尾部,結(jié)果還是一樣。 所以這里需要另外的操作來(lái)對(duì)js文件加載進(jìn)行優(yōu)化.

defer加載

這是HTML4中定義的一個(gè)script屬性,它用來(lái)表示的是,當(dāng)渲染引擎遇到script的時(shí)候,如果script引用的是外部資源,則會(huì)暫時(shí)掛起,并進(jìn)行加載。 渲染引擎繼續(xù)解析下面的HTML文檔,解析完時(shí),則會(huì)執(zhí)行script里面的腳本。

他的支持度是<=IE9的.
并且,他的執(zhí)行順序,是嚴(yán)格依賴的,即:


當(dāng)頁(yè)面解析完后,他便會(huì)開始按照順序執(zhí)行 outside1 和 outside2文件。
如果你在IE9以下使用defer的話,可能會(huì)遇到 它們兩個(gè)不是順序執(zhí)行的,這里需要一個(gè)hack進(jìn)行處理,即在兩個(gè)中間加上一個(gè)空的script標(biāo)簽


 //hack

但是,如果你將defer屬性用在inline的script腳本里面,在Chrome和FF下是沒有效果的。
即:

async加載

async是H5新定義的一個(gè)script 屬性。 他是另外一種js的加載模式。

渲染引擎解析文件,如果遇到script(with async)

繼續(xù)解析剩下的文件,同時(shí)并行加載script的外部資源

當(dāng)script加載完成之后,則瀏覽器暫停解析文檔,將權(quán)限交給JS引擎,指定加載的腳本。

執(zhí)行完后,則恢復(fù)瀏覽器解析腳本

可以看出async也可以解決 阻塞加載 這個(gè)問題。不過,async執(zhí)行的時(shí)候是異步執(zhí)行,造成的是,執(zhí)行文件的順序不一致。即:


這時(shí),誰(shuí)先加載完,就先執(zhí)行誰(shuí)。所以,一般依賴文件就不應(yīng)該使用async而應(yīng)該使用defer.
defer的兼容性比較差,為IE9+,不過一般是在移動(dòng)端使用,也就不存在這個(gè)problem了。
其實(shí),defer和async的原理圖,如圖一樣。(包括放在head中的script標(biāo)簽)

腳本異步

腳本異步是一些異步加載庫(kù)(比如require)使用的基本加載原理. 直接上代碼:

function asyncAdd(src){
    var script = document.createElement("script");
    script.src = src;
    document.head.appendChild(script);
}
//加載js文件
asyncAdd("test.js");

這時(shí)候,可以異步加載文件,不會(huì)造成阻塞的效果.
但是,這樣加載的js文件是無(wú)序的,無(wú)法正常加載依賴文件。
如果你想要js文件按照你自定義的順序執(zhí)行,則要將async設(shè)置為false. 但是會(huì)阻塞其它文件的加載

var asyncAdd = (function(){
    var head = document.head,
        script;
    return function(src){
        script = document.createElement("script");
        script.src= src;
        script.async=false;
        document.head.appendChild(script);
    }
})();
//加載文件
asyncAdd("first.js");
asyncAdd("second.js");
//或者簡(jiǎn)便一點(diǎn)
["first.js","second.js"].forEach((src)=>{async(src);});

但是,使用腳本異步加載的話,需要等待css文件加載完后,才開始進(jìn)行加載,不能充分利用瀏覽器的并發(fā)加載優(yōu)勢(shì)。而使用靜態(tài)文本加載async或者defer則不會(huì)出現(xiàn)這個(gè)問題。
使用腳本異步加載時(shí),只能等待css加載完后才會(huì)加載

使用靜態(tài)的async加載時(shí),css和js會(huì)并發(fā)一起加載

(from 妙凈)

關(guān)于這三種如何取舍,那就主要看leader給我們目標(biāo)是什么,是兼容IE8,9還是手機(jī)端,還是桌面瀏覽器,或者兩兩組合。
但是對(duì)于多帶帶使用某一個(gè)技能的場(chǎng)景,使用時(shí)需要注意一些tips。
js文件放置位置應(yīng)該放置到body末尾
如果使用async的話,最后加上defer以求向下兼容

 //如果兩者都支持,async會(huì)默認(rèn)覆蓋掉defer
//如果只支持一個(gè),則執(zhí)行對(duì)應(yīng)的即可

通常,我們使用的加載都是defer加載(因?yàn)楹軓?qiáng)的依賴關(guān)系).
但,上面的簡(jiǎn)單js文件依賴加載只針對(duì)于,依賴關(guān)系不強(qiáng),或者說,相互關(guān)聯(lián)性不強(qiáng)的js文件。先在js模塊化思想 已經(jīng)成為主流, 如果這樣手動(dòng)添加defer或者async是沒有太大的實(shí)際意義的。
原因就在于, 好復(fù)雜~
所以,才有了webpack,requireJS等模塊打包工具。這也是給我們?cè)谛阅芎徒Y(jié)構(gòu)上尋找一個(gè)平衡點(diǎn)的嘗試。
這里也給大家安利一些建議:
業(yè)務(wù)邏輯代碼使用模塊化書寫, 測(cè)試代碼或者監(jiān)聽代碼使用async,或者defer填充。 這也是比較好的實(shí)踐。

深入腳本異步加載

最簡(jiǎn)單的腳本異步就是在head里添加一個(gè)script標(biāo)簽.

var asyncAdd = (function(){
    var head = document.head,
        script;
    return function(src){
        script = document.createElement("script");
        script.async=false;
        document.head.appendChild(script);
    }
})();
asyncAdd("test.js"); //異步加載文檔

這樣寫,其實(shí)還不如,直接加async. 這樣簡(jiǎn)單的異步加載,是不能滿足我們模塊化書寫的龐大業(yè)務(wù)邏輯的。 這里,我們將一步一步的優(yōu)化我們的代碼,實(shí)現(xiàn),異步j(luò)s文件加載的模塊化.

串行加載js文件

對(duì)上述簡(jiǎn)單js異步腳本的升級(jí)版就是使用串行方式,加載js腳本。首先,我們需要了解一下,DOMreadyState和onload事件,這里先安利一下Nicholas大神 推薦的一份檢測(cè)onload的腳本:

function loadScript(url, callback){

    var script = document.createElement("script")
    script.type = "text/javascript";

    if (script.readyState){  //IE
        script.onreadystatechange = function(){
            if (script.readyState == "loaded" ||
                    script.readyState == "complete"){
                script.onreadystatechange = null; //解除引用
                callback();
            }
        };
    } else {  //Others
        script.onload = function(){
            callback();
        };
    }

    script.src = url;
    document.body.appendChild(script);
}

但從IE11開始,已經(jīng)支持onload事件, 不過,現(xiàn)在這份代碼的價(jià)值還是非常大的, 目前主流兼容IE8+。
當(dāng)然,我們可以使用loadScript中進(jìn)行回調(diào)加載.

loadScript("test1.js",loadScript("test2.js",loadScript("test3.js")));

不過,這簡(jiǎn)直就是沒人性的寫法。 所以,這里我們可以進(jìn)行優(yōu)化一下。我們可以使用以前的模式,進(jìn)行重構(gòu),這里我選擇命令模式和鏈?zhǔn)秸{(diào)用。
直接貼代碼吧:

 var loadJs = (function() {
        var script = document.createElement("script");
        if (script.readyState) {
            return function(url, cb) {
                script = document.createElement("script");
                script.src = url;
                document.body.appendChild(script);
                script.onreadystatechange = function() {
                    if (script.readyState == "loaded" ||
                        script.readyState == "complete") {
                        script.onreadystatechange = null; //解除引用
                        cb();
                    }
                };
            }
        } else {
            return function(url, cb) {
                script = document.createElement("script");
                script.src = url;
                document.body.appendChild(script);
                script.onload = function() {
                    cb();
                };
            }
        }
    })();

    //測(cè)試用例: commandJs.add("test.js",[test.js,test1.js]).exe();
    //或者 commandJs.add("test.js").add("test1.js").add([test1.js,test2,js]).exe();
    var commandJs = (function() {
        var group = [],
            len = 0;
        //類型檢測(cè)
        //數(shù)組
        var isArray = function(para) {
                return (para instanceof Array);
            }
            //String類型
        var isString = function(para) {
                return Object.prototype.toString.call(para) === "[object String]";
            }
            //集合檢測(cè)
        var correctType = function(para) {
                return isString(para) || isArray(para);
            }
            //添加src內(nèi)容
        var add = function() {
            for (var i = 0, js; js = arguments[i++];) {
                if (!correctType(js)) {
                    throw new Error(`the ${i}th js file"s type is not correct`);
                }
                group.push(js);
            }
            return this;
        }
        var isFinish = function() {
                len--;
                if (len === 0) {
                    exe(); //開始加載下一組js文件
                }
            }
            //并行加載js文件
        var loadArray = function(urls) {
            urls.forEach((url) => {
                loadJs(url, (function() {
                    isFinish(); //判斷是否執(zhí)行完全
                }).bind(this));
            });
        }
        var exe = function() {
            if (group.length === 0) return; //遍歷完所有的urls時(shí),退出執(zhí)行
            var js = group.shift();
            if (isArray(js)) {
                len = js.length;
                loadArray(js);
            } else {
                len = 1;
                loadArray([js]);
            }
            return this;
        }
        return {
            exe,
            add
        }
    })();

OK, 我們來(lái)驗(yàn)證一樣,串行執(zhí)行的測(cè)試結(jié)果:

commandJs.add("./js/loader01.js").add("./js/loader02.js").exe();
//或者
commandJs.add("./js/loader01.js","./js/loader02.js").exe();
//這兩種寫法都是可以的

最后的結(jié)果是:

ok~ 可以通過,這樣可以自定義加載很多依賴文件。 但是,造成結(jié)果是,時(shí)間成本耗費(fèi)太大。 有時(shí)候, 一個(gè)主文件的main 有很多依賴js模塊, 那么我們考慮一下,能否把這些js模塊并行加載進(jìn)來(lái)呢?

其實(shí),上面的那一串代碼,已經(jīng)將串行和并行給結(jié)合起來(lái)了。那并行是怎么做的呢? 其實(shí)就是,同時(shí)向頁(yè)面中添加script tag然后監(jiān)聽,是否所有的tag都已經(jīng)加載完整。如果是,開始加載下一組js文件。

其實(shí),最主要的代碼塊是這里:

  var isFinish = function() {
                len--;
                if (len === 0) {
                    exe(); //開始加載下一組js文件
                }
            }
            //并行加載js文件
        var loadArray = function(urls) {
            urls.forEach((url) => {
                loadJs(url, (function() {
                    isFinish(); //判斷是否執(zhí)行完全
                }).bind(this));
            });
        }
//執(zhí)行順序就是,然后中間加了一些trick進(jìn)行,類型的判斷.
//exe=> loadArray => isFinish ~>exe
并行加載js

OK, 上面我們已經(jīng)測(cè)試了js的異步加載,這里我們測(cè)試一下js并行加載的效果:

commandJs.add(["./js/loader01.js","./js/loader02.js"]).exe();

上圖時(shí)間:

我們對(duì)比一下異步加載的:

從上面很容易知道,異步和同步加載的區(qū)別,因?yàn)檫@個(gè)文件較小體現(xiàn)的價(jià)值不是很大,我們換一個(gè)比較大的文件進(jìn)行加載:

//并行:
 commandJs.add(["http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min.js","https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react-dom-server.js"]).exe();
 //串行
commandJs.add("http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min.js","https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react-dom-server.js").exe();

看一下圖:
//并行

//串行

大家可以鉤鉤手指,算一下兩者的時(shí)間差, 一個(gè)是取max, 一個(gè)是取add. 結(jié)果是顯而易見的。 當(dāng)然,模塊加載插件比如requireJS,labJS,他們所要做的功能比這里的要豐滿的多, 當(dāng)你 多個(gè)文件引入同一個(gè)依賴的時(shí)候,只需要加載一次(判斷唯一性), 以及引用模塊的ID 的 標(biāo)識(shí)等。
js 腳本異步加載還有很多方法,比如xhr, iframe ,以及使用img 的 src進(jìn)行加載,這些都是可行的, 但是他們的局限性也很大, xhr,iframe的同域要求,使用img還不如直接使用script。 我這里列一下他們的大概情況表吧

加載方式 實(shí)現(xiàn)效果
xhr 腳本并行下載,要求同域,不會(huì)阻塞其他資源
iframe 要求同域,腳本并行下載,不阻塞其他資源,但損耗較大,目前業(yè)界推崇淘汰
img 慘無(wú)人道,大家知道有就行了

其實(shí),大家看到這里也就可以了。下文,主要是我對(duì)上面代碼的一個(gè)優(yōu)化,或者說是Promise實(shí)踐. 由于懶得開篇幅了,所以就直接接著寫。

使用Promise異步加載

前面說了,如果使用像loadScript這種,直接進(jìn)行回調(diào)串行的話,造成的結(jié)果是,callback hell;

loadScript("test1.js",loadScript("test2.js",loadScript("test3.js")));

如果了解Promise的童鞋,應(yīng)該知道,使用Promise就可以完全解決這個(gè)問題。 這里,我們使用Promise對(duì)上面進(jìn)行代碼進(jìn)行重構(gòu)

var loadJs = (function() {
        var script = document.createElement("script");
        if (script.readyState) {
            return function(url) {
                return new Promise(function(res, rej) {
                    script = document.createElement("script");
                    script.src = url;
                    document.body.appendChild(script);
                    script.onreadystatechange = function() {
                        if (script.readyState == "loaded" ||
                            script.readyState == "complete") {
                            script.onreadystatechange = null; //解除引用
                            res();
                        }
                    };
                })
            }
        } else {
            return function(url) {
                return new Promise(function(res, rej) {
                    script = document.createElement("script");
                    script.src = url;
                    document.body.appendChild(script);
                    script.onload = function() {
                        res();
                    };
                })
            }
        }
    })();

接著,我們來(lái)調(diào)用代碼看看:

 loadJs("http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min.js")
    .then(function(){
        return loadJs("./js/loader01.js");
    }).then(function(){
        console.log("finish loading");
    })

結(jié)果是:

那如果我們想并行加載的話,怎么辦呢? 很簡(jiǎn)單使用Promise提供的all函數(shù)就可以了.
show u the code:

Promise.all([loadJs("http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min.js"),loadJs("./js/loader01.js")])

結(jié)果為:

OK~ 平時(shí),我們加載模塊的時(shí)候,就可以使用Promise來(lái)進(jìn)行練習(xí),這樣可以減少很多不必要的邏輯代碼。簡(jiǎn)直,贊~(≧▽≦)/~

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/78680.html

相關(guān)文章

  • 【前端構(gòu)建】RequireJS及其優(yōu)化工具

    摘要:介紹一款模塊加載工具的入門,并且重點(diǎn)介紹其優(yōu)化工具。發(fā)布目錄項(xiàng)目源代碼工具目錄,例如構(gòu)建工具等。另外,前端代碼發(fā)布前都會(huì)進(jìn)行壓縮,使文件足夠小。原來(lái)是因?yàn)槔锪?,所以?yōu)化工具把也合并進(jìn)來(lái)了。而優(yōu)化工具要用好,要多嘗試他們的配置選項(xiàng)。 前端變化太快,如今RequireJS已經(jīng)無(wú)法吸引眼球了。介紹一款模塊加載工具:RequireJS的入門,并且重點(diǎn)介紹其優(yōu)化工具。 一、RequireJS簡(jiǎn)介...

    Loong_T 評(píng)論0 收藏0
  • 不簡(jiǎn)單的前端性能優(yōu)化

    摘要:本文主要介紹關(guān)鍵渲染路徑與網(wǎng)絡(luò)兩個(gè)方面的性能優(yōu)化并提供,篇幅較長(zhǎng)建議電腦觀看。百度統(tǒng)計(jì)代碼注意,的腳本不會(huì)被阻塞,完成后立即執(zhí)行,但是有可能會(huì)阻塞關(guān)鍵渲染路徑。 本文主要介紹關(guān)鍵渲染路徑與網(wǎng)絡(luò)兩個(gè)方面的性能優(yōu)化并提供demo,篇幅較長(zhǎng)建議電腦觀看。 前端優(yōu)化的方面太多,本文介紹的僅僅是其中的一部分,力求涵蓋關(guān)鍵渲染路徑的方方面面,及一些不常被提到的網(wǎng)絡(luò)優(yōu)化部分。 測(cè)試環(huán)境如無(wú)特殊說明均...

    RobinQu 評(píng)論0 收藏0
  • 不簡(jiǎn)單的前端性能優(yōu)化

    摘要:本文主要介紹關(guān)鍵渲染路徑與網(wǎng)絡(luò)兩個(gè)方面的性能優(yōu)化并提供,篇幅較長(zhǎng)建議電腦觀看。百度統(tǒng)計(jì)代碼注意,的腳本不會(huì)被阻塞,完成后立即執(zhí)行,但是有可能會(huì)阻塞關(guān)鍵渲染路徑。 本文主要介紹關(guān)鍵渲染路徑與網(wǎng)絡(luò)兩個(gè)方面的性能優(yōu)化并提供demo,篇幅較長(zhǎng)建議電腦觀看。 前端優(yōu)化的方面太多,本文介紹的僅僅是其中的一部分,力求涵蓋關(guān)鍵渲染路徑的方方面面,及一些不常被提到的網(wǎng)絡(luò)優(yōu)化部分。 測(cè)試環(huán)境如無(wú)特殊說明均...

    developerworks 評(píng)論0 收藏0
  • 不簡(jiǎn)單的前端性能優(yōu)化

    摘要:本文主要介紹關(guān)鍵渲染路徑與網(wǎng)絡(luò)兩個(gè)方面的性能優(yōu)化并提供,篇幅較長(zhǎng)建議電腦觀看。百度統(tǒng)計(jì)代碼注意,的腳本不會(huì)被阻塞,完成后立即執(zhí)行,但是有可能會(huì)阻塞關(guān)鍵渲染路徑。 本文主要介紹關(guān)鍵渲染路徑與網(wǎng)絡(luò)兩個(gè)方面的性能優(yōu)化并提供demo,篇幅較長(zhǎng)建議電腦觀看。 前端優(yōu)化的方面太多,本文介紹的僅僅是其中的一部分,力求涵蓋關(guān)鍵渲染路徑的方方面面,及一些不常被提到的網(wǎng)絡(luò)優(yōu)化部分。 測(cè)試環(huán)境如無(wú)特殊說明均...

    hedge_hog 評(píng)論0 收藏0
  • 淺談webpack4.0 性能優(yōu)化

    摘要:中在性能優(yōu)化所做的努力,也大抵圍繞著這兩個(gè)大方向展開。因此,將依賴模塊從業(yè)務(wù)代碼中分離是性能優(yōu)化重要的一環(huán)。大型庫(kù)是否可以通過定制功能的方式減少體積。這又違背了性能優(yōu)化的基礎(chǔ)。接下來(lái)可以抓住一些細(xì)節(jié)做更細(xì)的優(yōu)化。中,為默認(rèn)啟動(dòng)這一優(yōu)化。 前言:在現(xiàn)實(shí)項(xiàng)目中,我們可能很少需要從頭開始去配置一個(gè)webpack 項(xiàng)目,特別是webpack4.0發(fā)布以后,零配置啟動(dòng)一個(gè)項(xiàng)目成為一種標(biāo)配。正因?yàn)?..

    leanxi 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<