聊一聊原生瀏覽器中的模組

發表於2018-07-04

自從ES2015定稿以來,我們通過 Babel 等轉換工具可以在專案中直接使用【模組】。前端模組化開發已經是不可逆轉,在 ECMAScript module 之前我們通過 requirejsseajsLABjs,甚至最早的時候我們通過閉包來實現模組化開發。目前一些主流的的瀏覽器廠商已經在他們新版的瀏覽器中原生支援了【模組】,今天我們就來原生瀏覽器中的模組到底如何。

目前原生支援模組用法的瀏覽器有:

  • Safari 10.1
  • Chrome 61
  • Firefox 60
  • Edge 16

要使用原生瀏覽器的模組,你只需要在 script 標籤上新增一個 type=module 屬性, 瀏覽器就會把這個指令碼(內聯指令碼或者外聯指令碼)當作模組來處理。

線上Demo

不支援裸匯入(不能通過模組名直接匯入)

一個合格的模組識別符號必須滿足下列條件之一:

  • 一個完整的非相對URL。通過 new URL(moduleSpecifier) 使用時不會報錯。
  • / 開頭。
  • ./ 開頭。
  • ../ 開頭。

保留其他說明符供將來使用,如匯入內建模組。

通過 nomodule 向後相容

如果當前瀏覽器支援 type=module 標籤的話會自動忽略 nomodule 標籤。這意味著你可以將模組暴露給支援模組的瀏覽器,同時可以給不支援模組的瀏覽器提供相容方案。

線上Demo

預設延遲載入

當網路狀況不好的時候,指令碼載入會阻塞瀏覽器解析 HTML。通常我們可以通過在 script 標籤上使用 defer 屬性來解決阻塞問題,但是這也會造成指令碼只有在文件解析完成後才執行,同時還要兼顧其他延遲指令碼的執行順序。預設情況下模組指令碼的表現類似於 defer — 它不會阻塞 HTML 的解析。

模組指令碼的執行佇列與使用了 defer 的常規指令碼一致。

線上Demo

內聯模組也是延遲載入的

唱過指令碼會忽略 defer 然而內聯模組總是 defer 的,不管它是否引入了動西。

線上Demo

內聯/外聯 模組都支援非同步載入

在普通指令碼中,async 能讓指令碼的下載不阻塞HTML的解析並在下載完成後儘快執行。和普通指令碼不同,內聯模組指令碼支援非同步載入的。

同樣的,非同步載入的模組可能不會按照它們在DOM中出現的順序執行。

線上Demo

模組只執行一次

如果你使用過ES6的模組, 那麼你肯定知道你可以多次引入同一模組但是他們只會執行一次。在Html中也一樣, 一個URL模組指令碼在一個頁面中只會執行一次。

線上Demo

遵循 CORS

不同於普通指令碼,跨站引用模組指令碼(及其引入)需要遵循CORS。 這意味著跨源模組指令碼必須返回有效的CORS頭,例如Access-Control-Allow-Origin:*。

線上Demo

不需要憑證

針對同源請求,大部分基於CORS的API需要請求帶上憑證(如:cookie),但是 fetch() 和模組指令碼是個例外,他們預設不會帶上相關憑證除非你明確指定。

如果你想在同源請求模組指令碼時帶上憑證,可以設定 crossorigin 屬性。如果跨站請求也想帶上的話,可以設定 crossorigin="use-credentials",需要注意的是跨站的站點需要在請求返回頭中加上 Access-Control-Allow-Credentials: true

線上Demo

這裡還有一個關於 模組只執行一次 的坑。當你通過一個URL引入一個模組時,如果一開始你以無憑證的方式請求,然後又以有憑證的方式再請求一次,你得到的返回都是無憑證請求的那次。這就是我為什麼會在第二次請求時在URL後加上,用於區分兩次請求。

更新:以上可能很快會改變。 預設情況下,fetch()和模組指令碼都會向相同來源的URL傳送憑據。

Mime-types

與普通指令碼不同,模組指令碼必須提供有效的JavaScript MIME型別,否則它們將不會執行。 HTML標準建議使用 text/javascript

相關文章