1. 瀏覽器載入 (1) 同步載入 在網頁中,瀏覽器載入js文件的方式是通過<script>標簽。如下所示: <script>標簽很方便,只要加入後,瀏覽器便可讀取並運行,但是在讀取的時候,瀏覽器是按照<script>標簽的出現順序,讀取Javascript文件,然後立即運行,導致在多個文件相互依賴 ...
1. 瀏覽器載入
(1) 同步載入
在網頁中,瀏覽器載入js文件的方式是通過<script>標簽。如下所示:
//內嵌腳本 <script type="text/javacript"> // code here! </script> //載入外部腳本 <script type="text/javascript src="path/demo.js"></script>
<script>標簽很方便,只要加入後,瀏覽器便可讀取並運行,但是在讀取的時候,瀏覽器是按照<script>標簽的出現順序,讀取Javascript文件,然後立即運行,導致在多個文件相互依賴的情況下,依賴性最小的文件必須放在最前面,依賴性最大的必須放在最後面,否則代碼會報錯,這一點,想必大家在使用bootstrap的時候都深有體會。另一方面,瀏覽器採用同步模式載入<script>標簽,也就是說,頁面會等待JavaScript文件載入完成,然後再運行後面的代碼。當存在很多個<script>標簽時,瀏覽器無法同時讀取,必須讀完一個再讀取另一個,造成讀取時間大大延長,頁面響應緩慢,影響用戶體驗。同步模式又稱阻塞模式,會阻止瀏覽器的後續處理,停止後續的解析,只有當前載入完成,才能進行下一步操作,所以預設同步執行才是安全的。但這樣如果js中有輸出document內容、修改DOM、重定向等行為,就會造成阻塞。所以一般建議把<script>標簽放在<body>結尾處,這樣能減少頁面阻塞。
(2)非同步載入
為瞭解決這一問題,ES5中採用了DOM方法,動態載入JavaScript腳本文件。
function loadScript(url) { var script = document.createElement("script"); script.type="text/javascript"; script.src=url; document.body.appendChild(script); }
這種方式通過創建一個新的<script>標簽,並設置其src屬性,非同步讀取javacript文件
這樣不會造成頁面阻塞,但會有另一個問題,如果其他腳本文件依賴於它,此時無法保證此腳本什麼時候能夠載入完畢。
另一種載入方式是利用defer和async屬性,使腳本非同步載入。渲染引擎遇到這一行命令,就會開始下載外部腳本,但不會等它下載和執行,而是直接執行後面的命令。defer和async的區別是: defer要等到整個頁面在記憶體中正常渲染結束(DOM結構完全生成,以及其他腳本執行完成),才會執行;async一旦下載完成,渲染引擎就會中斷渲染,執行這個腳本以後,再繼續渲染。即defer是渲染完再執行,async是下載完就執行。另外,如果有多個defer腳本,會按照它們在頁面中出現的順序載入,而多個async腳本是不能保證載入順序的。
IE9及以下版本在延遲實現方面存在一些相當糟糕的錯誤,導致執行順序無法保證。 如果你需要支持<= IE9,我建議不要使用defer,如果執行順序很重要,請包含沒有屬性的腳本。<script src="path/demo.js" defer></script> <script src="path/demo.js" async></script>
如何選用defer和async。如果使用的script是個模塊,並且不依賴任何其它script文件時使用async;如果該腳本依賴其它script或則被其它script依賴,就使用defer;倘若腳本文件很小且被一個async script依賴,就使用內嵌script把該文件放在所有async script前面。
另外一種方法是onload事件的非同步載入。
(function(){ if(window.attachEvent) { window.attachEvent("load", asyncLoad); } else if(window.addEventListener) { window.addEventListener("load", asyncLoad); } else {
window.onload = asyncLoad;
} var asyncLoad = function() { var script = document.createElement("script"); script.type="text/javascript"; script.async = true; script.src = ('https:'==document.location.protocol ? 'https://ssl' : 'http:www') + '.baidu.com/demo.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(script, s); }; )();
這種方法是把插入script的方法放在一個函數裡面,然後放在window的onload方法裡面執行,這樣就解決了阻塞onload事件的觸發問題。
由於Javascript的動態性,還有很多非同步載入方法:XHR Injection、XHR eval、Script In Iframe、document.write("<script type='text/javascript' src=' '")等;
XHR註入:通過XMLHttpRequest來獲取Javascript,然後創建一個script元素插入到DOM結構中。ajax請求成功後設置script.text為請求成功後返回的responseText.
var createXHR = function() { var obj; if(window.XMLHttpRequest) obj = new XMLHttpRequest(); else obj = new ActiveObject("Microsoft.XMLHTTP"); return obj; }; var xhr = createXML(); xhr.open("GET", "http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js", true); xhr.send(); xhr.onreadystatechange = function() { if(xhr.readyState == 4 && xhr.status == 200) { var script = document.createElement("script"); script.text = xhr.requestText; document.getElementsByTagName("head")[0].appendChild(script); } }
XHR eval(): 與XHR Injection對responseText的執行方式不同,直接把responseText放在eval()函數裡面執行。
var createXHR = function() { var obj; if(window.XMLHttpRequest) obj = new XMLHttpRequest(); else obj = new ActiveObject("Microsoft.XMLHTTP"); return obj; }; var xhr = createXML(); xhr.open("GET", "http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js", true); xhr.send(); xhr.onreadystatechange = function() { if(xhr.readyState == 4 && xhr.status == 200) { eval(xhr.responseText); $('#btn').click(function() { alert($(this).text()); }); } }
Script In Iframe: 在父視窗插入一個iframe元素,然後再iframe中執行載入JS的操作。
var insertJS = function(){ alert($); }; var iframe = document.createElement("iframe"); document.body.appendChild(iframe); var doc = iframe.contentWindow.document;//獲取iframe中的window doc.open(); doc.write("<script>var insertJS = function(){};<\/script><body onload='insertJS()'></body>"); doc.close();
2. 延遲載入
有些JS代碼在某些情況下需要使用,並不是頁面初始化的時候就要用到。延遲載入就是為解決這個問題。將JS切分成許多模塊,頁面初始化時只將事件處理程式添加到UI元素上,然後其它JavaScript代碼的載入延遲到第一次用戶交互或者其他條件需要用到的時候再載入。類似圖片的懶載入。這樣做的目的是可以漸進式地載入頁面,儘可能快地為用戶提供目前需要的信息,其餘部分的內容可以在用戶瀏覽該頁面時在後臺載入。
JavaScript的載入分為兩個部分:下載和執行腳本,非同步載入只解決了下載的問題,但是代碼在下載完成後就可能會立即執行,在執行過程中瀏覽器儲與阻塞狀態,響應不了任何需求。為瞭解決JavaScript延遲載入的問題,可以利用非同步載入緩存起來,所以不會立即執行,然後在第一次需要的時候再執行。
第二部分內容的載入可以用創建動態腳本的形式:
window.onload = function() { var script = document.createElement("script"); script.type="text/javascript"; script.src="demo.js"; document.documentElement.firstChild.appendChild("script"); }
3. 按需載入
可以通過創建一個require方法,包含需要載入的腳本名稱和附加腳本載入完成後需要執行的回調函數。
function require(file, callback) { var script = document.getElementsByTagName("script")[0]; var newjs = document.createElement("script"); newjs.onload= function() { callback(); }; newjs.src=file; script.parentNode.insertBefore(newjs, script); }
參考:
https://www.jb51.net/article/107680.htm
http://es6.ruanyifeng.com/#docs/module-loader