[譯] 瀏覽器中的 ECMAScript 模塊

原文連接: ECMAScript modules in browsers

做者:Jake Archibaldjavascript

瀏覽器如今可使用 ES 模塊(module)了!它們是:html

  • Safari 10.1
  • Chrome 61
  • Firefox 60
  • Microsoft Edge 16
<script type="module">
  import {addTextToBody} from './utils.mjs';

  addTextToBody('Modules are pretty cool.');
</script>
// utils.mjs
export function addTextToBody(text) {
  const div = document.createElement('div');
  div.textContent = text;
  document.body.appendChild(div);
}

在線演示java

您只須要在 script 元素上添加 type=module,瀏覽器就會將內聯腳本或外部腳本做爲 ECMAScript module 處理。git

關於模塊(module)已經有一些很棒的文章,可是我想分享一些在我測試和閱讀規範的時候學到的瀏覽器特有的內容。es6

目前還不支持的某些 import 用法

// 已支持:
import {foo} from 'https://jakearchibald.com/utils/bar.mjs';
import {foo} from '/utils/bar.mjs';
import {foo} from './bar.mjs';
import {foo} from '../bar.mjs';

// 不支持:
import {foo} from 'bar.mjs';
import {foo} from 'utils/bar.mjs';

有效的模塊路徑說明符必須符合下列條件之一:github

  • 一個完整的非相對URL,這樣在將其傳給 new URL(moduleSpecifier) 的時候纔不會報錯。
  • / 開頭的。
  • ./ 開頭的。
  • ../ 開頭的。

其餘形式的說明符保留供未來使用,例如導入內置模塊。web

使用 nomodule 來向後兼容

<script type="module" src="module.mjs"></script>
<script nomodule src="fallback.js"></script>

在線演示跨域

支持 type=module 的瀏覽器會忽略屬性爲 nomodule 的腳本。這意味着您能夠給支持模塊的瀏覽器提供模塊樹,同時給其餘瀏覽器提供一個降級版本。瀏覽器

瀏覽器問題

  • Firefox 瀏覽器不支持 nomodule (issue)。已在 Firefox nightly 中修復!
  • Edge 瀏覽器不支持 nomodule (issue)。已在 Edge 16 中修復!
  • Safari 瀏覽器不支持 nomodule。已在 Safari 11 中修復!對於 10.1,這裏有一個很是聰明的替代辦法

默認狀況下延遲執行

<!-- 這個腳本的執行會晚於… -->
<script type="module" src="1.mjs"></script>

<!-- …這個腳本… -->
<script src="2.js"></script>

<!-- …可是會在這個腳本以前執行。 -->
<script defer src="3.js"></script>

在線演示cookie

執行的順序是:2.js1.mjs3.js

script 在獲取期間會阻塞 HTML 解析器,簡直太糟糕了。對於常規腳本,您可使用 defer 來避免阻塞,固然這也會推遲腳本的執行,直到文檔完成解析,並與其餘延遲腳本一塊兒維護執行順序。模塊腳本的默認表現行爲就像 defer ——當它正在獲取時,沒有辦法讓一個模塊腳本阻塞 HTML 解析器。

模塊腳本使用和添加了 defer 的常規腳本相同的執行隊列。

內聯腳本也是延時的

<!-- 這個腳本的執行會晚於… -->
<script type="module">
  addTextToBody("Inline module executed");
</script>

<!-- …這個腳本… -->
<script src="1.js"></script>

<!-- …和這個腳本… -->
<script defer>
  addTextToBody("Inline script executed");
</script>

<!-- …可是會在這個腳本以前執行。 -->
<script defer src="2.js"></script>

在線演示

執行順序是1.js ,內聯腳本,內聯腳本,2.js

常規的內聯腳本會忽略 defer ,然而內聯模塊腳本卻老是被延遲,不管它們有沒有導入任何東西。

Async 對內聯、外部模塊一樣適用

<!-- 一旦獲取了導入,就會執行此操做 -->
<script async type="module">
  import {addTextToBody} from './utils.mjs';

  addTextToBody('Inline module executed.');
</script>
<!-- 一旦獲取了腳本和它的導入,就會執行此操做 -->
<script async type="module" src="1.mjs"></script>

在線演示

快速下載的腳本會在慢速下載的腳本以前執行。

與常規腳本同樣,async 會讓腳本在下載過程當中不會阻塞 HTML 解析器,而且儘快地執行。與常規腳本不一樣,async 也適用於內聯模塊。

與往常的 async 同樣,腳本不會按照它們出如今 DOM 中的順序執行。

瀏覽器問題

  • Firefox 瀏覽器不支持內聯模塊腳本上的 async (issue)。已在 Firefox 59 中修復!

模塊僅執行一次

<!-- 1.mjs 僅執行一次 -->
<script type="module" src="1.mjs"></script>
<script type="module" src="1.mjs"></script>
<script type="module">
  import "./1.mjs";
</script>

<!-- 然而,普通的腳本卻執行屢次 -->
<script src="2.js"></script>
<script src="2.js"></script>

在線演示

若是您理解 ES 模塊,您就會知道您雖然能夠引入它們不少次,可是它們卻僅僅會執行一次。固然,這一樣適用於HTML中的腳本模塊 - 特定URL的模塊腳本每頁只執行一次。

瀏覽器問題

  • Edge 執行屢次模塊 (issue)。已修復,可是還沒發佈(但願 Edge 17 會帶上這個修復內容)。

老是使用 CORS

<!-- 該腳本不會執行, 由於它不能經過 CORS 檢查 -->
<script type="module" src="https://….now.sh/no-cors"></script>

<!-- 該腳本不會執行, 由於它引入的腳本之一不能經過 CORS 檢查 -->
<script type="module">
  import 'https://….now.sh/no-cors';

  addTextToBody("This will not execute.");
</script>

<!-- 該腳本會執行,由於它經過了 CORS 檢查 -->
<script type="module" src="https://….now.sh/cors"></script>

在線演示

與常規腳本不一樣,模塊腳本(及其引入的內容)是經過 CORS 獲取的。這就意味着跨域的模塊腳本必須返回有效的 CORS 響應頭 ,好比 Access-Control-Allow-Origin: *

瀏覽器問題

  • Firefox 加載 Demo 頁面失敗 (issue)
  • Edge 加載沒有 CORS header 的模塊腳本 (issue)。 已在 Edge 16 中修復!

不攜帶憑據

<!-- 攜帶憑據獲取(cookie 等) -->
<script src="1.js"></script>

<!-- 不攜帶憑據獲取 -->
<script type="module" src="1.mjs"></script>

<!-- 攜帶憑據獲取 -->
<script type="module" crossorigin src="1.mjs?"></script>

<!-- 不攜帶憑據獲取 -->
<script type="module" crossorigin src="https://other-origin/1.mjs"></script>

<!-- 攜帶憑據獲取 -->
<script type="module" crossorigin="use-credentials" src="https://other-origin/1.mjs?"></script>

在線演示

若是請求來自相同的源,大多數基於 CORS 的 API 會發送憑據(cookie 等),可是 fetch() 和模塊腳本倒是例外的——非您要求它們,不然它們不會發送憑據除。

您能夠經過添加 crossorigin 屬性來向同源模塊添加憑據(這對我來講彷佛有點奇怪,我在規範中對此提出質疑)。若是您打算向其餘的源也發送憑據,使用 crossorigin="use-credentials"。注意其餘源必須使用 Access-Control-Allow-Credentials:true 的響應頭來響應。

此外,還有一個與「模塊只執行一次」規則相關的問題。模塊由其URL標記,所以若是首次請求了一個模塊而不攜帶憑據,而後再次攜帶憑據請求該模塊,那麼第二次得到的依然是不攜帶憑證的模塊。 這就是爲啥我在上面的URL中使用 問號 ? 的緣由,使它們成爲惟一的。

更新: 上面的狀況可能很快就會發生改變。fetch() 和模塊腳本默認都會向同源的 URL 發送憑據。Issue

瀏覽器問題

  • Chrome 使用憑據請求同源模塊(issue。已在 Chrome 61 中修復!
  • Safari 即便添加了 crossorigin 屬性,也不使用憑據請求同源模塊(issue)。
  • Edge 即便添加了 crossorigin 屬性,也不使用憑據請求同源模塊(issue。已在 Edge 16 中修復!
  • Edge 默認請求同源模塊的時候攜帶了憑據(issue)。

MIME 類型

不一樣於常規腳本,模塊腳本必須是有效的 JavaScript MIME 類型中的一種類型,不然模塊就不會執行。HTML 標準建議使用 text/javascript

瀏覽器問題

  • Edge 執行無效的 MIME 類型腳本(issue

這就是我目前學到的內容啦。毋庸置疑,我對 ES 模塊登錄瀏覽器感到很是興奮!

性能建議,動態導入等等!

請查閱有關 Web Fundamentals 的文章,深刻了解模塊使用狀況。

相關文章
相關標籤/搜索