深入理解JS異步編程五(腳本異步加載)

異步腳本加載

阻塞性腳本

JavaScript在瀏覽器中被解析和執行時具有阻塞的特性,也就是說,當JavaScript代碼執行時,頁面的解析、渲染以及其他資源的下載都要停下來等待腳本執行完畢

瀏覽器是按照從上到下的順序解析頁面,因此正常情況下,JavaScript腳本的執行順序也是從上到下的,即頁面上先出現的代碼或先被引入的代碼總是被先執行,即使是允許並行下載JavaScript文件時也是如此。注意我們這裏標紅了”正常情況下”,原因是什麼呢?我們知道,在HTML中加入JavaScript代碼有多種方式,概括如下(不考慮require.js或sea.js等模塊加載器):

(1)正常引入:即在頁面中通過<script>標籤引入腳本代碼或者引入外部腳本
(2)通過document.write方法向頁面寫入<script>標籤或代碼
(3)通過動態腳本技術,即利用DOM接口創建<script>元素,並設置元素的src,然後再將元素添加進DOM中。
(4)通過Ajax獲取腳本內容,然後再創建<script>元素,並設置元素的text,再將元素添加進DOM中。
(5)直接把JavaScript代碼寫在元素的事件處理程序中或直接作爲URL的主體

具體參考 http://www.jb51.net/article/77920.htm

腳本延遲運行

一般在JS頁面延遲執行一些方法。可以使用以下的方法:


Window.setTimeout  

jQuery.delay

jQuery.queue和jQuery.dequeue
<script src="deferdemo.js" defer></script>

加上 defer 等於在頁面完全在入後再執行,相當於 window.onload ,但應用上比 window.onload 更靈活!

<script type="text/javascript" src="demo_async.js" async="async"></script>

使用async屬性,瀏覽器會下載js文件,同時繼續對後面的內容進行渲染
通常如果js不需要改變DOM結構時可以使用async進行異步加載(比如一些統計代碼可以異步加載,因爲此代碼與頁面執行邏輯無關,不會改變DOM結構)

SeaJS與RequireJS

網上寫amd和cmd的文章很多,當然也有很多都是誤人子弟的片面的看法,所以還是推薦自己看官方文檔多加嘗試去理解。

“RequireJS 遵循的是 AMD(異步模塊定義)規範,SeaJS 遵循的是 CMD (通用模塊定義)規範”。

AMD 是 RequireJS 在推廣過程中對模塊定義的規範化產出。
CMD 是 SeaJS 在推廣過程中對模塊定義的規範化產出。

amd 規劃 https://github.com/amdjs/amdjs-api/wiki/AMD-(%E4%B8%AD%E6%96%87%E7%89%88)

cmd 規範 https://github.com/seajs/seajs/issues/242

區別:

  1. 對於依賴的模塊,AMD 是提前執行,CMD 是延遲執行。不過 RequireJS 從 2.0 開始,也改成可以延遲執行(根據寫法不同,處理方式不同)

  2. CMD 推崇依賴就近,AMD 推崇依賴前置。

ECMAScript6 Moudle

歷史上,JavaScript一直沒有模塊(module)體系,無法將一個大程序拆分成互相依賴的小文件,再用簡單的方法拼裝起來。其他語言都有這項功能,比如Ruby的require、Python的import,甚至就連CSS都有@import
到了ES6,實現了模塊化的功能,功能上基本可以取代 cmd和amd的規範,

模塊的功能主要由兩個命令構成,export和import,export命令用於規定模塊的對外接口,import命令用於輸入其他模塊提供的功能。

export的寫法,

// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;

export {firstName, lastName, year};

上面代碼在export命令後面,使用大括號指定所要輸出的一組變量。

import寫法:

// main.js

import {firstName, lastName, year} from './profile';

function setName(element) {
  element.textContent = firstName + ' ' + lastName;
}

ES6模塊加載的實質

ES6模塊加載的機制,與CommonJS模塊完全不同。CommonJS模塊輸出的是一個值的拷貝,而ES6模塊輸出的是值的引用。CommonJS模塊輸出的是被輸出值的拷貝,也就是說,一旦輸出一個值,模塊內部的變化就影響不到這個值

ES6模塊的運行機制與CommonJS不一樣,它遇到模塊加載命令import時,不會去執行模塊,而是隻生成一個動態的只讀引用。等到真的需要用到時,再到模塊裏面去取值,換句話說,ES6的輸入有點像Unix系統的”符號連接“,原始值變了,import輸入的值也會跟着變。因此,ES6模塊是動態引用,並且不會緩存值,模塊裏面的變量綁定其所在的模塊。

// mod.js
function C() {
  this.sum = 0;
  this.add = function () {
    this.sum = 1;
  };
  this.show = function () {
    console.log(this.sum);
  }
}

export let c = new C();

上面的腳本mod.js,輸出的是一個C的實例。不同的腳本加載這個模塊,得到的都是同一個實例

// x.js
import {c} from './mod';
c.add();

// y.js
import {c} from './mod';
c.show();

// main.js
import './x';
import './y';

現在執行main.js,輸出的是1。
證明加載的是同一個實例
參考 http://es6.ruanyifeng.com/#docs/module

發佈了48 篇原創文章 · 獲贊 29 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章