多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > web前端 > htmlcss > JavaScript學習--Item26 異步的腳本加載

JavaScript學習--Item26 異步的腳本加載

來源:程序員人生   發布時間:2016-06-13 11:46:46 閱讀次數:3288次

先來看這行代碼:

<script src = "allMyClientSideCode.js"></script>

這有點兒……不怎樣樣。“這該放在哪兒?”開發人員會奇怪,“靠上點,放到<head>標簽里?還是靠下點,放到<body>標簽里?”這兩種做法都會讓富腳本站點的下場很凄慘。<head>標簽里的大腳本會滯壓所有頁面渲染工作,使得用戶在腳本加載終了之前1直處于“白屏死機”狀態。而<body>標簽末尾的大腳本只會讓用戶看到毫無生命力的靜態頁面,本來應當進行客戶端渲染的地方卻散布著不起作用
的控件和空空如也的方框。

完善解決這個問題需要對腳本分而治之:那些負責讓頁面更好看、更好用的腳本應當立即加載,而那些可以待會兒再加載的腳本稍后再加載。但是怎樣才能既滯壓這些腳本,又能保證它們在被調用時的可用性呢?

1、<script>標簽的再認識

現代閱讀器中的<script>標簽分成了兩種新類型:經典型和非阻塞型。接下來討論如何應用這兩種標簽來盡快加載頁面。

1、阻塞型腳本何去何從?

標準版本的<script>標簽常常被稱作阻塞型標簽。這個詞必須放在上下文中進行理解:現代閱讀器看到阻塞型<script>標簽時,會跳過阻塞點繼續讀取文檔及下載其他資源(腳本和樣式表)。但直到腳本下載終了并運行以后,閱讀器才會評估阻塞點以后的那些資源。因此,如果網頁文檔的<head>標簽里有5 個阻塞型<script>標簽,則在所有這5 個腳本均下載終了并運行之前,用戶除頁面標題以外看不到任何東西。不但如此,即使這些腳本運行了,它們也只能看到阻塞點之前的那部份文檔。如果想看到<body>標簽中正等待加載的那些好東西,就必須給像document.onreadystatechange 這樣的事件綁定1個事件處理器。

基于上述緣由,現在愈來愈流行把腳本放在頁面<body>標簽的尾部。這樣,1方面用戶可以更快地看到頁面,另外一方面腳本也能夠主動密切接觸DOM 而無需等待事件來觸發自己。對大多數腳本而言,這次“搬家”是個巨大的進步。

但并不是所有腳本都1樣。在向下搬動腳本之前,請先問自己2 個問題。

  • 該腳本是不是有可能被<body>標簽里的內聯JavaScript 直接調用?答案可能1目了然,但仍值得核對1遍。

  • 該腳本是不是會影響已渲染頁面的外觀?Typekit 宿主字體就是1個例子。如果把Typekit 腳本放在文檔末尾,那末頁面文本就會渲染兩次,即讀取文檔時即刻渲染,腳本運行時再次渲染。

上述問題只要有1個答案是肯定的,那末該腳本就應當放在<head>標簽中,否則就能夠放在<body>標簽中,文檔形如:

<html> <head> <!--metadata and stylesheets go here --> <script src="headScripts.js"></scripts> </head> <body> <!-- content goes here --> <script src="bodyScripts.js"></script> </body> </html>

這確切大大縮短了加載時間,但要注意1點,這可能讓用戶有機會在加載bodyScripts.js 之前與頁面交互。

2、 腳本的提早加載與延遲運行

上面建議將大多數腳本放在<body>中,由于這樣既能讓用戶更快地看到網頁,又能避免操控DOM之前綁定“就緒”事件的開消。但這類方式也有1個缺點,即閱讀器在加載完全個文檔之前沒法加載這些腳本,這對那些通過慢速連接傳送的大型文檔來講會是1大瓶頸

理想情況下,腳本的加載應當與文檔的加載同時進行,并且不影響DOM 的渲染。這樣,1旦文檔就緒就能夠運行腳本,由于已依照<script>標簽的次序加載了相應腳本。

如果大家已讀到這里了,那末1定會迫不及待地想寫1個自定義Ajax 腳本加載器以滿足這樣的需求!不過,大多數閱讀器都支持1個更加簡單的解決方案。

<script defer src = "deferredScript.js">

添加defer(延遲)屬性相當于對閱讀器說:“請馬上開始加載這個腳本吧,但是,請等到文檔就緒且所有此前具有defer 屬性的腳本都結束運行以后再運行它。”在文檔<head>標簽里放入延遲腳本,既能帶來腳本置于<body>標簽時的全部好處,又能讓大文檔的加載速度大幅提升!

不足的地方就是,并不是所有閱讀器都支持defer屬性。這意味著,如果想確保自己的延遲腳本能在文檔加載后運行,就必須將所有延遲腳本的代碼都封裝在諸如jQuery 之$(document).ready 之類的結構中。

上1節的頁面例子改進以下:

<html> <head> <!-- metadata and stylesheets go here --> <script src="headScripts.js"></scripts> <script defer src="http://www.vxbq.cn/upload/caiji/20160601/deferredScripts.js"></script> </head> <body> <!-- content goes here --> </body> </html>

請記住deferredScripts 的封裝很重要,這樣即便閱讀器不支持defer,deferredScripts 也會在文檔就緒事件以后才運行。如果頁面主體內容遠遠超過幾千字節,那末付出這點代價是完全值得的。

3、 腳本的并行加載

如果你是瑣屑較量到毫秒級頁面加載時間的完善主義者,那末defer或許就像是淡而無味的薄鹽醬油。你可不想1直等到此前所有的defer 腳本都運行結束,固然也肯定不想等到文檔就緒以后才運行這些腳本,你就是想盡快加載并且盡快運行這些腳本。這也正是現代閱讀器提供了async(異步)屬性的緣由。

<script async src = "speedyGonzales.js"> <script async src = "roadRunner.js">

如果說defer 讓我們想到1種靜靜等待文檔加載的有序排隊場景,那末async 就會讓我們想到混亂的無政府狀態。前面給出的那兩個腳本會以任意次序運行,而且只要JavaScript 引擎可用就會立即運行,而不論文檔就緒與否。

對大多數腳本來講,async 是1塊難以下咽的雞肋。async 不像defer那樣得到廣泛的支持。同時,由于異步腳本會在任意時刻運行,它實在太容易引發海森堡蟻蟲之災了(腳本恰好結束加載時就會蟻蟲4起)。

當我們加載1些第3方腳本,而且也不在意它們誰先運行誰后運行。因此,對這些第3方腳本使用async 屬性,相當于1分錢沒花就提升了它們的運行速度。

上1個頁面示例再添加兩個獨立的第3方小部件,得到的結果以下:

<html> <head> <!-- metadata and stylesheets go here --> <script src="headScripts.js"></scripts> <script src="http://www.vxbq.cn/upload/caiji/20160601/deferredScripts.js" defer></script> </head> <body> <!-- content goes here --> <script async defer src="feedbackWidget.js"></script> <script async defer src="chatWidget.js"></script> </body> </html>

這個頁面結構清晰展現了腳本的優先次序。對絕大多數閱讀器,DOM的渲染只會延遲至headScripts.js 結束運行時。進行DOM渲染的同時會在后臺加載deferredScripts.js。接著,在DOM 渲染結束時將運行deferredScripts.js 和那兩個小部件腳本。這兩個小部件腳本在那些支持async 的閱讀器中會做無序運行。如果不肯定這是不是妥當,請勿使用async!

2、可編程的腳本加載

雖然<script>標簽簡單得使人心動,但有些情況確切需要更精致的腳本加載方式。我們可能只想給那些滿足1定條件的用戶加載某個腳本,比方白金會員或到達1定級別的玩家,也可能只想當用戶單擊激活時才加載某個特性,比方聊天小部件。

1、直接加載腳本

我們可以用類似下面這樣的代碼來插入<script>標簽。

var head = document.getElementsByTagName('head')[0]; var script = document.createElement('script'); script.src = '/js/feature.js'; head.appendChild(script);

稍等,我們如何才能知道腳本什么時候加載結束呢?我們可以給腳本本身添加1些代碼以觸發事件,但如果要為每一個待加載腳本都添加這樣的代碼,那也太鬧心了。或是另外1種情況,即我們不可能給第3方服務器上的腳本添加這樣的代碼。HTML5 規范定義了1個可以綁定回調的onload 屬性。

script.onload = function() { // 現在可以調用腳本里定義的函數了 };

不過, IE8 及更老的版本其實不支持onload , 它們支持的是onreadystatechange。某些閱讀器在插入<script>標簽時還會出現1些“靈異事件”。而且,這里乃至還沒談到毛病處理呢!為了不
所有這些使人頭疼的問題,在此強烈建議使用腳本加載庫。

3、yepnope的條件加載

yepnope是1個簡單的、輕量級的腳本加載庫(緊縮后的精簡版只有1.7KB),其設計目標就是真誠服務于最多見的動態腳本加載需求。

yepnope 最簡單的用法是,加載腳本并對腳本完成運行這1事件返回1個回調。

yepnope({ load: 'oompaLoompas.js', callback: function() { console.log('oompa-Loompas ready!'); } });

還是無動于中?下面我們要用yepnope 來并行加載多個腳本并按給定次序運行它們。舉個例子,假定我們想加載Backbone.js,而這個腳本又依賴于Underscore.js。為此,我們只需用數組情勢提供這兩個腳本的位置作為加載參數。

yepnope({ load: ['underscore.js', 'backbone.js'], complete: function() { // 這里是Backbone 的業務邏輯 } });

請注意,這里使用了complete(完成)而不是callback(回調)。

其差別在于,腳本加載列表中的每一個資源均會運行callback,而只有當所有腳本都加載完成后才會運行complete。yepnope 的標志性特點是條件加載。給定test 參數,yepnope 會根據該參數值是不是為真而加載不同的資源。舉個例子,可以以1定的準確度判斷用戶是不是在用觸摸屏裝備,從而據此相應地加載不同的樣式表及腳本。

yepnope({ test: Modernizr.touch, yep: ['touchStyles.css', 'touchApplication.js'], nope: ['mouseStyles.css', 'mouseApplication.js'], complete: function() { // 不論是哪種情況,利用程序均已就緒! } });

我們只用寥寥幾行代碼就搭好了舞臺,可以基于用戶的接入裝備而給他們完全不同的使用體驗。固然,不是所有的條件加載都需要備齊yep(是)和nope(否)這兩種測試結果。yepnope 最多見的用法之1就是加載墊片腳本以彌補老式閱讀器缺失的功能。

yepnope({ test: window.json,nope: ['json2.js'], complete: function() { // 現在可以放心腸用JSON 了 } });

頁面使用了yepnope 以后應當變成下面這類漂亮的標記結構:

<html> <head> <!-- metadata and stylesheets go here --> <script src="headScripts.js"></scripts> <script src="http://www.vxbq.cn/upload/caiji/20160601/deferredScripts.js" defer></script> </head> <body> <!-- content goes here --> </body> </html>

很眼熟?這個結構和討論defer 屬性那1節給出的結構1樣,唯1的區分是這里的某個腳本文件已拼接了yepnope.js(極可能就在deferredScripts.js 的頂部),這樣就能夠獨立地加載那些根據條件再加載的腳本(由于閱讀器需要墊片腳本)和那些想要動態加載的腳本(以便回利用戶的動作)。結果將是1個更小巧的deferredScripts.js。

4、Require.js/AMD 模塊化加載

開發人員想通過腳本加載器讓混亂不堪的富腳本利用變得更規整有序1些,而Require.js 就是這樣1種選擇。Require.js 這個強大的工具包能夠自動和AMD技術1起捋順哪怕最復雜的腳本依賴圖。

現在先來看1個用到Require.js 同名函數的簡單腳本加載示例。

require(['moment'], function(moment) { console.log(moment().format('dddd')); // 星期幾 });

require 函數接受1個由模塊名稱構成的數組,然后并行地加載所有這些腳本模塊。與yepnope 不同,Require.js 不會保證按順序運行目標腳本,只是保證它們的運行次序能滿足各自的依賴性要求,但條件是
這些腳本的定義遵照了AMD(Asynchronous Module Definition,異步模塊定義)規范。
案例1: 加載 JavaScript 文件

<script src="./js/require.js"></script> <script> require(["./js/a.js", "./js/b.js"], function() { myFunctionA(); myFunctionB(); }); </script>

如案例1 所示,有兩個 JavaScript 文件 a.js 和 b.js,里面各自定義了 myFunctionA 和 myFunctionB 兩個方法,通過下面這個方式可以用 RequireJS 來加載這兩個文件,在 function 部份的代碼可以援用這兩個文件里的方法。

require 方法里的這個字符串數組參數可以允許不同的值,當字符串是以”.js”結尾,或以”/”開頭,或就是1個 URL 時,RequireJS 會認為用戶是在直接加載1個 JavaScript 文件,否則,當字符串是類似”my/module”的時候,它會認為這是1個模塊,并且會以用戶配置的 baseUrl 和 paths 來加載相應的模塊所在的 JavaScript 文件。配置的部份會在稍后詳細介紹。

這里要指出的是,RequireJS 默許情況下并沒有保證 myFunctionA 和 myFunctionB 1定是在頁面加載完成以后履行的,在有需要保證頁面加載以后履行腳本時,RequireJS 提供了1個獨立的 domReady 模塊,需要去 RequireJS 官方網站下載這個模塊,它并沒有包括在 RequireJS 中。有了 domReady 模塊,案例1 的代碼稍做修改加上對 domReady 的依賴就能夠了。

案例2: 頁面加載后履行 JavaScript

<script src="./js/require.js"></script> <script> require(["domReady!", "./js/a.js", "./js/b.js"], function() { myFunctionA(); myFunctionB(); }); </script>

履行案例2的代碼后,通過 Firebug 可以看到 RequireJS 會在當前的頁面上插入為 a.js 和 b.js 分別聲明了1個 < script> 標簽,用于異步方式下載 JavaScript 文件。async 屬性目前絕大部份閱讀器已支持,它表明了這個 < script> 標簽中的 js 文件不會阻塞其他頁面內容的下載。

案例3:RequireJS 插入的 < script>

<script type="text/javascript" charset="utf⑻" async="" data-requirecontext="_" data-requiremodule="js/a.js" src="js/a.js"></script>

AMD推行1個由Require.js 負責提供的名叫define 的全局函數,該函數有3 個參數:

  • 模塊名稱,
  • 模塊依賴性列表,
  • 在那些依賴性模塊加載結束時觸發的回調。

使用 RequireJS 來定義 JavaScript 模塊

這里的 JavaScript 模塊與傳統的 JavaScript 代碼不1樣的地方在于它不必訪問全局的變量。模塊化的設計使得 JavaScript 代碼在需要訪問”全局變量”的時候,都可以通過依賴關系,把這些”全局變量”作為參數傳遞到模塊的實現體里,在實現中就避免了訪問或聲明全局的變量或函數,有效的避免大量而且復雜的命名空間管理。

猶如 CommonJS 的 AMD 規范所述,定義 JavaScript 模塊是通過 define 方法來實現的。

下面我們先來看1個簡單的例子,這個例子通過定義1個 student 模塊和1個 class 模塊,在主程序中實現創建 student 對象并將 student 對象放到 class 中去。

案例4: student 模塊,student.js

define(function(){ return { createStudent: function(name, gender){ return { name: name, gender: gender }; } }; });

案例5:class 模塊,class.js

define(function() { var allStudents = []; return { classID: "001", department: "computer", addToClass: function(student) { allStudents.push(student); }, getClassSize: function() { return allStudents.length; } }; } );

案例6: 主程序

require(["js/student", "js/class"], function(student, clz) { clz.addToClass(student.createStudent("Jack", "male")); clz.addToClass(student.createStudent("Rose", "female")); console.log(clz.getClassSize()); // 輸出 2 });

student 模塊和 class 模塊都是獨立的模塊,下面我們再定義1個新的模塊,這個模塊依賴 student 和 class 模塊,這樣主程序部份的邏輯也能夠包裝進去了。

案例7: 依賴 student 和 class 模塊的 manager 模塊,manager.js

define(["js/student", "js/class"], function(student, clz){ return { addNewStudent: function(name, gender){ clz.addToClass(student.createStudent(name, gender)); }, getMyClassSize: function(){ return clz.getClassSize(); } }; });

案例8:新的主程序

require(["js/manager"], function(manager) { manager.addNewStudent("Jack", "male"); manager.addNewStudent("Rose", "female"); console.log(manager.getMyClassSize());// 輸出 2 });

通過上面的代碼示例,我們已清楚的了解了如何寫1個模塊,這個模塊如何被使用,模塊間的依賴關系如何定義。

5、小結

要想讓自己的站點更快捷,可以異步加載那些暫時用不到的腳本。為此最簡單的做法是審慎地使用defer 屬性和async 屬性。如果要求根據條件來加載腳本,請斟酌像yepnope 這樣的腳本加載器。如果站點存在大量相互依賴的腳本,請斟酌Require.js。選擇最合適任務的工具,然后使用它,享受它帶來的便捷。

參考:

  • JavaScript異步編程
  • RequireJS模塊化加載
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 亚洲五月婷 | 精品久久国产 | 午夜成人影片 | 午夜爱爱免费视频 | 国产成人啪午夜精品网站 | 欧美午夜理伦三级在线观看 | 亚洲欧美日韩国产精品一区 | 亚色最新网址 | 日本a中文字幕 | 啪啪色视频 | 欧美性xxxx极品高清3d | 亚洲精品久久久久久久久久久网站 | 国产人成精品免费视频 | 日韩免费 | 手机看片高清国产日韩片 | 免费观看的黄色网址 | 中文字幕在线永久在线视频2020 | 亚洲小视频在线播放 | 手机在线看福利 | 国产精品亚洲欧美日韩区 | 美国毛片免费观看 | 亚洲免费在线播放 | 国产欧美综合在线一区二区三区 | 国产农村1级毛片 | 日本三区视频 | 欧美精品亚洲 | 国产精品99久久免费黑人 | 日韩欧美在线综合 | 性色在线播放 | 男女污视频在线观看 | 亚洲高清二区 | 99re热久久精品这里都是精品 | 欧美一区二区三区视视频 | 国产caob| 在线影院福利 | 久久69精品久久久久久hb | 精品国产一区二区三区四区不 | 美女免费视频是免费网站 | 图片专区亚洲 欧美 另类 | 色综合欧美综合天天综合 | 欧美理论在线 |