在瀏覽器中也可使用JavaScript modules(模塊功能)了。目前支持這一特性的瀏覽器包括:javascript
- Safari 10.1.
- 谷歌瀏覽器(Canary 60) – 須要在
chrome:flags
裏開啓」實驗性網絡平臺功能(Experimental Web Platform)」 - Firefox 54 – 須要在
about:config
裏開啓dom.moduleScripts.enabled
選項。 - Edge 15 – 須要在
about:flags
裏開啓 Experimental JavaScript Features 選項
<script type="module"> import {addTextToBody} from './utils.js'; addTextToBody('Modules are pretty cool.'); </script>
// utils.js export function addTextToBody(text) { const div = document.createElement('div'); div.textContent = text; document.body.appendChild(div); }
咱們須要作的只是在script標籤元素上聲明type=module
就能夠了,這樣,瀏覽器就能解析代碼中的module語法了。html
網上已經有不少關於modules的好文章了,但我在這裏想單獨分享一下專門針對瀏覽器裏的ECMAScript modules的知識,這些都是我在閱讀和測試ECMAScript規範時學到的。java
目前並不支持」裸」 import 語法
// 能夠這樣使用: import {foo} from 'https://jakearchibald.com/utils/bar.js'; import {foo} from '/utils/bar.js'; import {foo} from './bar.js'; import {foo} from '../bar.js'; // 這些寫法不支持: import {foo} from 'bar.js'; import {foo} from 'utils/bar.js';
下面幾種 module 語法是有效的:git
- 絕對路徑的URL。也就是說,使用
new URL(模塊地址)
也不會報錯。 - 地址開頭是
/
。 - 地址開頭是
./
。 - 地址開頭是
../
。
另一些語法是保留爲未來使用的,好比,導入內部的(built-in) modules。es6
用來保持向後兼容的nomodule
<script type="module" src="module.js"></script> <script nomodule src="fallback.js"></script>
可以認識type=module
語法的瀏覽器會忽略具備nomodule
屬性的scripts。也就是說,咱們可使用一些腳本服務於支持module語法的瀏覽器,同時提供一個nomodule
的腳本用於哪些不支持module語法的瀏覽器,做爲補救。github
瀏覽器問題
- Firefox並不支持
nomodule
屬性 (issue)。但在Firefox nightly版裏已經修復了這個問題。 - Edge並不支持
nomodule
屬性。 (issue). - Safari 10.1 並不支持
nomodule
屬性,但在最新的技術預覽版中修復了這個問題。對於10.1版, 有一個很聰明的變通技巧。
缺省設置爲Defer
<!-- 這個腳本的執行將會晚於… --> <script type="module" src="1.js"></script> <!-- …這個腳本… --> <script src="2.js"></script> <!-- …可是會先於這個腳本. --> <script defer src="3.js"></script>
執行的順序將會是2.js
, 1.js
, 3.js
.web
若是script代碼塊阻止HTML分析器下載其餘代碼,這是很是糟糕的事情,一般咱們會使用 defer
屬性來防止這種解析阻塞,但同時這樣也會延遲script腳本的執行——直到真個文檔解析完成。並且還要參考其它deferred script腳本的執行順序。Module scripts缺省行爲狀態很像 defer
屬性的做用 – 一個 module script 不會妨礙HTML分析器下載其它資源。chrome
Module scripts隊列的執行順序跟使用了defer
屬性的普通腳本隊列的執行順序同樣。api
Inline scripts一樣是deferred
<!-- 這個腳本的執行將會晚於… --> <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
, inline script, inline module, 2.js
.跨域
普通的inline scripts會忽略defer
屬性,而 inline module scripts 永遠是deferred的,無論它是否有 import 行爲。
外部 & inline modules script腳本上的 Async 屬性
<!-- 這個腳本將會在imports完成後當即執行 --> <script async type="module"> import {addTextToBody} from './utils.js'; addTextToBody('Inline module executed.'); </script> <!-- 這個腳本將會在腳本加載和imports完成後當即執行 --> <script async type="module" src="1.js"></script>
這個快速下載script會率先執行。
跟普通的scripts同樣, async
屬性能讓script加載的同時並不阻礙HTML解析器的工做,並且在加載完成後當即執行。跟普通的scripts不一樣的是, async
屬性在inline modules腳本上也有效。
由於永遠都是async
, 因此這些scripts的執行順序也許並不會像它們出如今DOM裏的順序。
瀏覽器問題
- Firefox並不支持inline module scripts上的
async
特性。(issue).
Modules只執行一次
<!-- 1.js 只執行一次 --> <script type="module" src="1.js"></script> <script type="module" src="1.js"></script> <script type="module"> import "./1.js"; </script> <!-- 而普通的腳本會執行屢次 --> <script src="2.js"></script> <script src="2.js"></script>
若是你知道 ES modules,你就應該知道,modules能夠import屢次,但只會執行一次。這種原則一樣適用於HTML裏的script modules – 一個肯定的URL上的module script在一個頁面上只會執行一次。
瀏覽器問題
- Edge瀏覽器會屢次執行 modules (issue).
CORS 跨域資源共享限制
<!-- 這個腳本不會執行,由於跨域資源共享限制 --> <script type="module" src="https://….now.sh/no-cors"></script> <!-- 這個腳本不會執行,由於跨域資源共享限制--> <script type="module"> import 'https://….now.sh/no-cors'; addTextToBody("This will not execute."); </script> <!-- 這個沒問題 --> <script type="module" src="https://….now.sh/cors"></script>
跟普通的scripts不一樣, module scripts (以及它們的 imports 行爲) 受 CORS 跨域資源共享限制。也就是說,跨域的 module scripts 必須返回帶有有效 Access-Control-Allow-Origin: *
的CORS頭信息。
瀏覽器問題
沒有憑證信息(credentials)
<!-- 有憑證信息 (cookies等) --> <script src="1.js"></script> <!-- 沒有憑證信息 --> <script type="module" src="1.js"></script> <!-- 有憑證信息 --> <script type="module" crossorigin src="1.js?"></script> <!-- 沒有憑證信息 --> <script type="module" crossorigin src="https://other-origin/1.js"></script> <!-- 有憑證信息--> <script type="module" crossorigin="use-credentials" src="https://other-origin/1.js?"></script>
當請求在同一安全域下,大多數的CORS-based APIs都會發送憑證信息 (cookies 等),但 fetch()
和 module scripts 例外 – 它們並不發送憑證信息,除非咱們要求它們。
咱們能夠經過添加crossorigin
屬性來讓同源module腳本攜帶憑證信息,若是你也想讓非同源module腳本也攜帶憑證信息,使用 crossorigin="use-credentials"
屬性。須要注意的是,非同源腳本須要具備 Access-Control-Allow-Credentials: true
頭信息。
一樣,「Modules只執行一次」的規則也會影響到這一特徵。URL是Modules的惟一標誌,若是你先請求的Modules沒有攜帶憑證信息,而後你第二次請求但願攜帶憑證信息,你仍然會獲得一個沒有憑證信息的module。這就是爲何上面的有個URL上我使用了一個?
號,是讓URL變的不一樣。
瀏覽器問題
- 谷歌瀏覽器在請求同源 modules 時帶有 credentials (issue).
- Safari 請求同源 modules 時不帶有 credentials,即便你使用了
crossorigin
屬性標誌。 (issue). - Edge瀏覽器徹底相反。它缺省狀態下對同源請求 modules 時帶有 credentials 而當你指定了
crossorigin
屬性後反倒沒了。 (issue).
火狐瀏覽器是惟一作的正確的瀏覽器,乾的漂亮!
Mime-types 文檔類型
跟普通的scripts不同,modules scripts 必須指定一個有效的 JavaScript MIME 類型,不然將不會執行。
瀏覽器問題
- Edge瀏覽器在無效MIME types下也執行 (issue).
這些就是目前我學到的。毋庸置疑,ES modules可以登錄瀏覽器,已經讓我興奮不已了。你呢?