原文連接node
與我交流過的絕大多數web開發者,都喜歡使用全部新的語法特性(如async/await,類,箭頭函數等)。儘管全部現代瀏覽器都支持以上的語法,多部分開發者仍然會轉譯到ES5而且加上polyfill以便支持哪一小部分仍舊使用老版本瀏覽器的用戶。webpack
這...有點糟。在理想的的世界中,是沒有沒必要要的代碼!git
新版本的JS和DOM接口能讓咱們選擇性地加載polyfill,由於在運行時,咱們能夠檢測瀏覽器對新特性的支持狀況。可是新的JS語法有一點很差,由於沒法識別的語法都會形成解析錯誤,致使沒有代碼會被執行。github
雖然如今並無對feature-detecting
這個語法的好的解決方案,但咱們確實有一個方法能作到ES2015語法支持的檢測。web
這就是<script type="module">
npm
多數開發者將<script type="module">
視爲加載ES模塊的一種方式(這沒毛病啊),但<script type="module">
也有更直接且實際的加載常規JavaScript文件與ES2015+功能,並知道瀏覽器能處理它!瀏覽器
換言之,全部支持<script type="module">
語法的瀏覽器也支持絕大多數你愛的那些ES2015+屬性。例如:緩存
剩下的事就是爲不支持<script type="module">
的瀏覽器提供一個降級的處理。若是你正在生成一個ES5版本的代碼,那麼恭喜你你已經作好這一步了,如今你須要作的就是生成一個ES2015+的版本!babel
本篇餘下的部分將討論如何實現這一技術以及發佈ES2015+代碼的能力將如何改變咱們編寫模塊的方式。async
若是你已經上手了webpack或rollup這樣的工具來生成你的JS,那就繼續吧!
下一步,在現有bundle的基礎上,你要生成第二份bundle。與第一份bundle的惟一區別就是你再也不須要把代碼轉譯到ES5版本,同時你也不用在引入任何polyfill。
在使用babel-preset-env的前提下,第二步是很是簡單的。你惟一要作的就是改變配置中的瀏覽器列表到支持<script type="module">
的瀏覽器,這樣Babel就不會進行那些沒必要要的轉譯。
換言之,用ES2015+的代碼取代ES5。
舉個例子,若是你在使用webpack,entry的路徑是./path/to/main.js
。目前的配置(要編譯成ES5版本的)應該大體以下(我會把這個bundle曾爲'古早版',由於它是ES6的):
module.exports = { entry: { 'main-legacy': './path/to/main.js', }, output: { filename: '[name].js', path: path.resolve(__dirname, 'public'), }, module: { rules: [{ test: /\.js$/, use: { loader: 'babel-loader', options: { presets: [ ['env', { modules: false, useBuiltIns: true, targets: { browsers: [ '> 1%', 'last 2 versions', 'Firefox ESR', ], }, }], ], }, }, }], }, };
要獲得一個現代的支持ES2015+的版本。你要作的僅僅是將目標環境改爲支持<script type="module">
的瀏覽器,像下面這樣:
module.exports = { entry: { 'main': './path/to/main.js', }, output: { filename: '[name].js', path: path.resolve(__dirname, 'public'), }, module: { rules: [{ test: /\.js$/, use: { loader: 'babel-loader', options: { presets: [ ['env', { modules: false, useBuiltIns: true, targets: { browsers: [ 'Chrome >= 60', 'Safari >= 10.1', 'iOS >= 10.3', 'Firefox >= 54', 'Edge >= 15', ], }, }], ], }, }, }], }, };
當你執行構建時,這兩個配置文件會獲得兩個JS文件:
下一步,就是更新你的HTML來支持選擇性加載JS bundle。你同時可使用<script type="module">
和<script nomodule>
來實現。
<!-- 支持ES模塊的瀏覽器去下載下面這個文件 --> <script type="module" src="main.js"></script> <!-- 老版本瀏覽器則加載下面這個文件 --> <script nomodule src="main-legacy.js"></script>
小貼士:可惡的Safari 10並不支持nomodule
屬性,不過你能夠在HTML前部引入 safari-nomodule.js來解決這一問題。(好在,在Safari 11種他們解決了這個問題,我到都拔出來了)
!
這多數狀況下,這個方法「能用」。可是在使用這一策略前,咱們須要瞭解模塊加載的一些細節。
<script defer>
語言同樣被加載,這就意味着。知道文件被解析前都不會被執行。若是你有一些代碼須要先行,請把它們拆分出來,而後單獨引用。嚴格模式
,因此若是出於某種緣由,你不要使用嚴格模式,請拆分出這部分代碼,並單獨引用。var foo = 'bar'
或是 函數聲明function foo() {…}
的變量能夠經過window.foo
訪問。但在一個模塊中卻並不是如此。因此這可能會成爲你書寫代碼時的一個坑!我建立了一個模版項目,讀者能夠看到這一方法在實際工做中的應用。
在這個模版中,我試用了許多新出的webpack特性,由於這個技術在實際工做中真的能用。擺脫,我可不是趙括。這些特性包括咱們常見的實踐:
我不會用本身不會的技術,若是你想要了解更多歡迎閱讀源代碼
若是你並不是使用webpack來生成生產環境的bundle,過程也大同小異。我之因此選擇webpack,由於它是當下最流行的,但它也是最複雜的!若是webpack能用,那麼其餘工具也能使用。
在我看來必須的,這些付出是值得的。下表比較了兩種版本最終生成文件的實際大小:
即使通過Gzip傳統ES5版本也是ES2015+版本體積的兩倍。
大致積文件不盡更耗費時間去加載,同時,也須要更長時間解析與執行。這兩個版本的解析/執行時間依舊是兩倍的關係。(這個測試我試用了webpagetest.org提供的 Moto G4)
雖然這些獨立的文件不大,解析/執行的時間也不是特別長,但這僅僅是個博客。若是是外頭那些龐然大物,ES2015+你絕對值得擁有!
一項來之HTTPArchive數據的統計顯示。Alexa排名前列的網站中有85181在他們的項目中使用了babel-polyfill
, core-js
, 或是regenerator-runtime
。6個月前這個數字是34588!
現實就是轉譯以及使用polyfill正迅速成爲新的標準。不幸的事,大部分用戶正所以犧牲了流量來下載這些原本能夠更小的文件。
如今的問題就是開發者並無發佈ES2015+版本的代碼,而是發佈了轉譯後的ES5版本。
但如今ES2015+是能夠部署的,因此是時候去改變了。
我徹底明白,這會帶來一些陣痛。 現在大多數構建工具發佈的文檔,都推薦ES5的配置。 這意味着,若是模塊做者開始向npm發佈ES2015 +源代碼,他們可能會破壞一些用戶的構建,這將會形成混亂。
問題是大多數使用Babel的開發人員將它配置爲不在node_modules
中傳輸任何內容,可是若是模塊是使用ES2015 +源代碼發佈的,則這是一個問題。 幸運的是修復很簡單。 您只需從構建配置中刪除node_modules排除:
rules: [ { test: /\.js$/, exclude: /node_modules/, // 移除這行 use: { loader: 'babel-loader', options: { presets: ['env'] } } } ]
弊端就是,babel
這樣的工具不只僅須要本地依賴關係,在執行時還須要在node_modules
中傳遞這種關係。這樣構件會變慢。不過這一問題能夠在持續化的本地緩存工具上獲得解決。
縱使前途坎坷,咱們也因該爲提高用戶體驗大步向前。經過發佈ES2015,咱們爲開發人員提供了一種選擇,並最終惠及每一個人。
<script type="module">
的價值遠不只僅是爲了在瀏覽器中加載ES模塊。
在支持這一特性的現代瀏覽器中,<script type="module">
能夠給予開發者,選擇性加載單一JS文件的預定體驗。
這與nomodule屬性一塊兒,爲咱們提供了一種在生產環境中使用ES2015+代碼的方法,咱們終於能夠中止向不須要它的用戶發送如此多的代碼。
編寫ES2015代碼對開發者來講是一個勝利,部署ES2015代碼對用戶來講是一個勝利。