高性能JavaScript:加載和執(zhí)行
在步入正題之前為大家介紹一本很好的書《高性能JavaScript》大家不妨看一下,在本文中我為大家分享我看這本書的一些收獲。
阻塞特性:
JS 有個(gè)很無語的阻塞特性,就是當(dāng)瀏覽器在執(zhí)行JS 代碼時(shí),不能同時(shí)做其他任何事情,無論其代碼是內(nèi)嵌的還是外部的。
腳本位置:
瀏覽器在碰到一個(gè)引入外部JS 文件的<script>標(biāo)簽時(shí)會(huì)停下所有工作來下載并解析執(zhí)行它,在這個(gè)過程中,頁面渲染和用戶交互完全被阻塞了,為了避免頁面加載時(shí)的停頓甚至空白頁的出現(xiàn),JS 腳本應(yīng)盡量放置在頁面底部,這點(diǎn)很重要:
- <html>
- <head>
- <title>無標(biāo)題文檔</title>
- <link rel="stylesheet" type="text/css" href="styles.css" />
- </head>
- <body>
- <p>頁面的內(nèi)容。。。</p>
- <!-- 推薦的位置,頁面底部: -->
- <script type="text/javascript" src="file1.js"></script>
- <script type="text/javascript" src="file2.js"></script>
- <script type="text/javascript" src="file3.js"></script>
- </body>
- </html>
組織腳本:
為了改善上面的阻塞情況,應(yīng)盡可能的減少頁面中<script>標(biāo)簽的出現(xiàn)次數(shù),這同時(shí)也是考慮到HTTP 請(qǐng)求會(huì)帶來額外的性能開銷,也就是說應(yīng)減少頁面中外鏈腳本的數(shù)量。
你可以手動(dòng)合并你的多個(gè)JS 文件,也可采用類似Yahoo! combo handler 這樣的實(shí)時(shí)在線服務(wù)來實(shí)現(xiàn),例如下面的這個(gè)<script>標(biāo)簽實(shí)際上便載入了3個(gè)JS 文件:
- <html>
- <head>
- <title>無標(biāo)題文檔</title>
- <link rel="stylesheet" type="text/css" href="styles.css" />
- </head>
- <body>
- <p>頁面的內(nèi)容。。。</p>
- <!-- 推薦的位置,頁面底部: -->
- <script type="text/javascript" src="http://yui.yahooapis.com/combo?file1.js&file2.js&file3.js"></script>
- </body>
- </html>
無阻塞的腳本:
為了阻塞狀況,這里提供了幾個(gè)實(shí)現(xiàn)并行下載JS 腳本的方案。
1. 延遲的腳本
HTML4 為<script>標(biāo)簽定義了一個(gè)defer 屬性,它能使這段代碼延遲執(zhí)行,然而該屬性只有IE4+ 和Firefox 3.5+ 支持。聲明了defer 屬性的<script>會(huì)在DOM加載完成,window.onload 事件觸發(fā)前被解析執(zhí)行:
- <script type="text/javascript" src="file1.js" defer></script>
2. 動(dòng)態(tài)腳本元素
這是最通用的解決方案,通過DOM 動(dòng)態(tài)地創(chuàng)建<script>元素并插入到文檔中,文件在該元素被添加到頁面時(shí)開始下載,這樣 無論在何時(shí)啟動(dòng)下載,文件的下載和執(zhí)行過程不會(huì)阻塞頁面其他進(jìn)程。
不過要注意使用這種方式加載的代碼會(huì)立刻執(zhí)行,這樣需清楚的了解各文件的作用以及合理的執(zhí)行順序,此時(shí)跟蹤并確保腳本下載完成并準(zhǔn)備就緒是很有必要的,非IE瀏覽器會(huì)在<script>元素接收完成時(shí)觸發(fā)一個(gè)load 事件,而IE 下則會(huì)觸發(fā)一個(gè)readystatechange 事件并通過readyState 屬性加以判斷便可。以下是兼容地動(dòng)態(tài)加載一個(gè)JS 腳本的函數(shù):了:
- function load_script(url, callback)
- { var script = document.createElement('script');
- script.type = 'text/javascript';
- if (script.readyState)
- { //IE
- script.onreadystatechange = functio()
- {
- if (script.readyState == 'loaded' || script.readyState == 'complete')
- {
- script.onreadystatechange = null;
- callback();
- }
- }
- }
- else
- { //others
- script.onload = function(){
- callback(); }
- }
- script.src = url;
- document.getElementsByTagName('head')[0].appendChild(script);
- }
你可以將這個(gè)函數(shù)保存至一個(gè)load_script.js 文件,然后用該函數(shù)來加載其他的腳本,當(dāng)要加載多個(gè)腳本時(shí),為了確保正確的加載順序,可以將load_script() 的執(zhí)行串聯(lián)起來,最后如前面說到的放至頁面的底部,這便是一個(gè)完美的解決方案了。
- <script type="text/javascript"src="load_script.js"></script>
- <script type="text/javascript">
- load_script('file1.js', function()
- {
- load_script('file2.js', function()
- {
- load_script('file3.js', functio() {
- //全部載入后的操作...
- } );
- } );
- } );
- </script>
3.XMLHttpRequest 腳本注入
即通過AJAX 方式加載,不過這種方式無法實(shí)現(xiàn)跨域加載,不適用于大型網(wǎng)站。
推薦的無阻塞模式
我們上面做的這些工作當(dāng)然也已經(jīng)被那些牛人們完成了,并寫成了一些優(yōu)秀的JS 類庫以便我們使用,它們均能很好地解決JS 腳本的阻塞問題,實(shí)現(xiàn)并行下載,例如: YUI3、LazyLoad、LABjs 等。
【編輯推薦】