如今,在項目中直接部署ES2015+代碼吧!

原文連接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+屬性。例如:緩存

  • async/await
  • 箭頭函數
  • fetch,Promise,Map,Set等

剩下的事就是爲不支持<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文件:

  • main.js (ES2015+語法)
  • main-legacy.js (ES5語法)

下一步,就是更新你的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種他們解決了這個問題,我到都拔出來了)

重要的思考

這多數狀況下,這個方法「能用」。可是在使用這一策略前,咱們須要瞭解模塊加載的一些細節。

  1. 模塊會像<script defer>語言同樣被加載,這就意味着。知道文件被解析前都不會被執行。若是你有一些代碼須要先行,請把它們拆分出來,而後單獨引用。
  2. 模塊會默認使用嚴格模式,因此若是出於某種緣由,你不要使用嚴格模式,請拆分出這部分代碼,並單獨引用。
  3. 模塊在頂層做用域中建立/聲明變量的行爲有別於腳本。在腳本中經過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了

如今的問題就是開發者並無發佈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代碼對用戶來講是一個勝利。

相關文章
相關標籤/搜索