亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

跟我學習javascript的異步腳本加載

2019-11-20 11:13:30
字體:
來源:轉載
供稿:網友

先來看這行代碼:

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

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

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

一、<script>標簽的再認識

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

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

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

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

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

  • 該腳本是否有可能被<body>標簽里的內聯JavaScript 直接調用?答案可能一目了然,但仍值得核查一遍。
  • 該腳本是否會影響已渲染頁面的外觀?Typekit 宿主字體就是一個例子。如果把Typekit 腳本放在文檔末尾,那么頁面文本就會渲染兩次,即讀取文檔時即刻渲染,腳本運行時再次渲染。

上述問題只要有一個答案是肯定的,那么該腳本就應該放在<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>

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

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

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

理想情況下,腳本的加載應該與文檔的加載同時進行,并且不影響DOM 的渲染。這樣,一旦文檔就緒就可以運行腳本,因為已經按照<script>標簽的次序加載了相應腳本。

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

<script defer src = "deferredScript.js">

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

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

上一節的頁面例子改進如下:

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

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

3、 腳本的并行加載

如果你是斤斤計較到毫秒級頁面加載時間的完美主義者,那么defer也許就像是淡而無味的薄鹽醬油。你可不想一直等到此前所有的defer 腳本都運行結束,當然也肯定不想等到文檔就緒之后才運行這些腳本,你就是想盡快加載并且盡快運行這些腳本。這也正是現代瀏覽器提供了async(異步)屬性的原因。

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

如果說defer 讓我們想到一種靜靜等待文檔加載的有序排隊場景,那么async 就會讓我們想到混亂的無政府狀態。前面給出的那兩個腳本會以任意次序運行,而且只要JavaScript 引擎可用就會立即運行,而不論文檔就緒與否。
對大多數腳本來說,async 是一塊難以下咽的雞肋。async 不像defer那樣得到廣泛的支持。同時,由于異步腳本會在任意時刻運行,它實在太容易引起海森堡蟻蟲之災了(腳本剛好結束加載時就會蟻蟲四起)。
當我們加載一些第三方腳本,而且也不在乎它們誰先運行誰后運行。因此,對這些第三方腳本使用async 屬性,相當于一分錢沒花就提升了它們的運行速度。
上一個頁面示例再添加兩個獨立的第三方小部件,得到的結果如下:

<html><head>  <!-- metadata and stylesheets go here -->  <script src="headScripts.js"></scripts>  <script src="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!
二、可編程的腳本加載
雖然<script>標簽簡單得令人心動,但有些情況確實需要更精致的腳本加載方式。我們可能只想給那些滿足一定條件的用戶加載某個腳本,譬如白金會員或達到一定級別的玩家,也可能只想當用戶單擊激活時才加載某個特性,譬如聊天小部件。
1、直接加載腳本
我們可以用類似下面這樣的代碼來插入<script>標簽。

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

稍等,我們如何才能知道腳本何時加載結束呢?我們可以給腳本本身添加一些代碼以觸發事件,但如果要為每個待加載腳本都添加這樣的代碼,那也太鬧心了?;蛘呤橇硗庖环N情況,即我們不可能給第三方服務器上的腳本添加這樣的代碼。HTML5 規范定義了一個可以綁定回調的onload 屬性。

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

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

三、yepnope的條件加載
yepnope是一個簡單的、輕量級的腳本加載庫(壓縮后的精簡版只有1.7KB),其設計目標就是真誠服務于最常見的動態腳本加載需求。
yepnope 最簡單的用法是,加載腳本并對腳本完成運行這一事件返回一個回調。

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 會根據該參數值是否為真而加載不同的資源。舉個例子,可以以一定的準確度判斷用戶是否在用觸摸屏設備,從而據此相應地加載不同的樣式表及腳本。

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

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

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="deferredScripts.js" defer></script></head><body>  <!-- content goes here --></body></html>

很眼熟?這個結構和討論defer 屬性那一節給出的結構一樣,唯一的區別是這里的某個腳本文件已經拼接了yepnope.js(很可能就在deferredScripts.js 的頂部),這樣就可以獨立地加載那些根據條件再加載的腳本(因為瀏覽器需要墊片腳本)和那些想要動態加載的腳本(以便回應用戶的動作)。結果將是一個更小巧的deferredScripts.js。
四、Require.js/AMD 模塊化加載
開發人員想通過腳本加載器讓混亂不堪的富腳本應用變得更規整有序一些,而Require.js 就是這樣一種選擇。Require.js 這個強大的工具包能夠自動和AMD技術一起捋順哪怕最復雜的腳本依賴圖。
現在先來看一個用到Require.js 同名函數的簡單腳本加載示例。

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

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

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

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

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

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

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

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

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

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

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

使用 RequireJS 來定義 JavaScript 模塊
這里的 JavaScript 模塊與傳統的 JavaScript 代碼不一樣的地方在于它無須訪問全局的變量。模塊化的設計使得 JavaScript 代碼在需要訪問”全局變量”的時候,都可以通過依賴關系,把這些”全局變量”作為參數傳遞到模塊的實現體里,在實現中就避免了訪問或者聲明全局的變量或者函數,有效的避免大量而且復雜的命名空間管理。
如同 CommonJS 的 AMD 規范所述,定義 JavaScript 模塊是通過 define 方法來實現的。
下面我們先來看一個簡單的例子,這個例子通過定義一個 student 模塊和一個 class 模塊,在主程序中實現創建 student 對象并將 student 對象放到 class 中去。
案例四: student 模塊,student.js

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

案例五:class 模塊,class.js

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

案例六: 主程序

 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 模塊都是獨立的模塊,下面我們再定義一個新的模塊,這個模塊依賴 student 和 class 模塊,這樣主程序部分的邏輯也可以包裝進去了。
案例七:依賴 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();     }    }; });

案例八:新的主程序

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

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

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

以上就是關于javascript的異步腳本加載的全部內容,想對大家的學習有所幫助。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲男人天堂网| 亚洲一区二区三区毛片| 中文字幕日韩综合av| 伊人久久精品视频| 亚洲xxx视频| 97精品伊人久久久大香线蕉| 国产精品久久激情| 成人午夜一级二级三级| 国产精品av电影| 97精品国产97久久久久久免费| 欧美与黑人午夜性猛交久久久| 亚洲网站在线观看| 亚洲二区在线播放视频| 成人性生交大片免费看小说| 日韩精品高清在线观看| 国产热re99久久6国产精品| 久久久久久一区二区三区| 国内精品久久久久久| 美女久久久久久久| 美女精品久久久| 精品国产乱码久久久久久婷婷| 亚洲aa中文字幕| 热久久视久久精品18亚洲精品| 精品美女永久免费视频| 久久国产精品影片| 777午夜精品福利在线观看| 色悠悠国产精品| 国产精品视频网址| 久久人人爽人人爽人人片av高请| 精品国产电影一区| 国产精品久久久久久久久久尿| 亚洲精品国产精品国自产观看浪潮| 久久精品99久久香蕉国产色戒| 影音先锋欧美在线资源| 国自产精品手机在线观看视频| 亚洲欧洲一区二区三区在线观看| 亚洲视频电影图片偷拍一区| 米奇精品一区二区三区在线观看| 久久久久国产精品www| 国产精品视频在线观看| 日本道色综合久久影院| 国产成人中文字幕| 欧美激情视频网| 91久久在线播放| 亚洲激情国产精品| 中文字幕av日韩| 日韩一区av在线| 欧美日韩国产精品一区二区不卡中文| 久久免费成人精品视频| 久久精品视频在线观看| 国产一区二区在线免费| 国产精品入口日韩视频大尺度| 欧美黑人极品猛少妇色xxxxx| 亚洲精品影视在线观看| 欧美伦理91i| 国产成人jvid在线播放| 日韩av资源在线播放| 久久精品国产久精国产思思| 欧美精品一二区| 日韩中文在线视频| www.亚洲天堂| 亚洲福利视频网站| 亚洲福利在线播放| 91亚洲国产成人久久精品网站| 69影院欧美专区视频| 国产第一区电影| 日本亚洲欧洲色| 久久99久久99精品免观看粉嫩| 动漫精品一区二区| 亚洲国产私拍精品国模在线观看| 在线一区二区日韩| 亚洲精品不卡在线| 久久国产精品网站| 国产精品久久久久久久电影| 精品亚洲夜色av98在线观看| 性亚洲最疯狂xxxx高清| 国产精品影片在线观看| 国内精品美女av在线播放| 亚洲成人黄色网| 成人自拍性视频| 国产精品99久久久久久久久| 欧美一区二区色| 欧美中文字幕在线| 欧美日韩国产精品一区二区不卡中文| 中文字幕亚洲一区二区三区五十路| 欧美日韩一区二区在线播放| 国产精品女主播视频| 色av吧综合网| 亚洲r级在线观看| 高清欧美性猛交xxxx| 欧美xxxx14xxxxx性爽| 国产免费一区二区三区在线观看| 欧美大片va欧美在线播放| 亚洲偷欧美偷国内偷| 国产精品吊钟奶在线| 亚洲综合中文字幕68页| 91精品国产99久久久久久| 国产69精品久久久久9999| 欧美性猛交xxxx免费看| 亚洲精品久久久久久下一站| 国产精品久久久久久久久久久久久| 国产精品青青在线观看爽香蕉| 欧美影院久久久| 亚洲精品久久久久久下一站| 78色国产精品| 欧美专区在线观看| 国产在线高清精品| 国产精品久久久久久久久久99| 91精品在线影院| 欧美一级高清免费| 欧美中文在线免费| 色综合久久天天综线观看| 亚洲人成自拍网站| 欧美国产亚洲精品久久久8v| 萌白酱国产一区二区| 日本午夜精品理论片a级appf发布| 欧美成人国产va精品日本一级| 91极品女神在线| 欧美激情视频一区二区三区不卡| 精品国偷自产在线视频99| 久久久免费观看| 国产精品普通话| 国产精品91在线| 亚洲一区二区久久久久久| 久久精品99国产精品酒店日本| 97精品国产97久久久久久春色| 精品国产一区久久久| 欧美日韩亚洲激情| 国产999精品久久久| 九九九久久久久久| 欧美大片在线免费观看| 国产精品男人的天堂| 国产精品国产福利国产秒拍| 在线看国产精品| 国产精品va在线播放| 欧美专区中文字幕| 欧美午夜xxx| 欧亚精品中文字幕| 国产专区欧美专区| 伊人伊人伊人久久| 国产亚洲美女精品久久久| 亚洲色图15p| 琪琪亚洲精品午夜在线| 欧美重口另类videos人妖| 国产精品午夜一区二区欲梦| 日韩女在线观看| 欧美日韩一区二区三区| 国产福利视频一区| 亚洲乱亚洲乱妇无码| 尤物九九久久国产精品的分类| 国产精品日韩欧美| 亚洲欧美在线磁力| 国产精品成人观看视频国产奇米| 日韩电影大全免费观看2023年上| 亚洲最大av网| 日韩av在线精品| 久久久免费观看视频| 日本国产一区二区三区| 中文字幕亚洲自拍| 日韩国产激情在线| 91国内免费在线视频| 欧美国产日韩xxxxx| 91中文在线观看| 国产日韩欧美夫妻视频在线观看|