1. 程式人生 > >深入理解JS非同步程式設計五(指令碼非同步載入)

深入理解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的主體

指令碼延遲執行

一般在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 在推廣過程中對模組定義的規範化產出。

區別:

  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';