從零學腳手架(四)---babel

莫問今朝·發表於2021-03-11

如果此篇對您有所幫助,在此求一個star。專案地址: OrcasTeam/my-cli

接下來介紹一個打包編譯過程中一個極為重要的工具--babel

ES6的枷鎖

細心的朋友可以知道,在之前打包編譯測試都是使用簡單的ES5特性,

並沒有使用過ES6(ES2015+)特性(import除外)

這是因為webpack本身不會處理程式碼中的ES6(ES2015+)特性,所以也就沒有使用。

先來做一個測試

/src/index.js 檔案使用部分ES6(ES2015+),檢視打包編譯程式碼會發現webpack並沒有處理ES6(ES2015+)特性。

從零學腳手架(四)---babel

自從ES6(ES2015+)時代來臨後,前端才具有了飛速發展。ES6(ES2015+)各種特性也給開發人員帶來了便利。

毫不客氣的說,沒有人再想寫ES5程式碼了。

但是,前端程式碼的執行環境(瀏覽器)是由使用者決定的,如果使用者一直使用舊版本瀏覽器,那麼新特性就無法執行在使用者瀏覽器中。

這時候就需要一種工具:將程式碼使用的ES6(ES2015+)特性轉換為ES5特性

這個工具就叫做:babel

?? ? webpack作為一個打包器。為babel提供了擴充套件支援。

?? ES6ES2015+所有版本統稱 有的文章會寫成ES7ES8。但其實都是ES6

? 上面程式碼使用到了ES6Promise型別塊級宣告(const)箭頭函式for-of語法陣列APIawait屬性,不瞭解ES6的朋友可以學習阮一峰老師的ES6入門教程

babel

babel介紹

ES6來臨後,前端開啟了百花綻放的時代。從而也導致了ES6ES5的工作並不僅僅侷限於JS語言的原始特性。

例如:TypescriptJSX語法等等。

這些都可以使用babel進行處理。

babel的設計思想也與webpack一致:提供核心引擎 + 外掛化的模式管理

babel提供了一個核心引擎庫:@babel/core 和 擴充套件外掛庫配置。

@babel/cli

babel 其實並不是webpack一個擴充套件外掛,它是一個獨立的工具。可以進行單獨配置、執行。

babel提供了一個@babel/cli庫,與webpack-cli庫一樣,允許命令列直接執行babel

從零學腳手架(四)---babel
{
  "scripts": {
	"build": "babel src -d lib"
  }
}

在此就不介紹@babel/cli這一塊的內容了,有興趣的朋友可以去官網學習

??? babel作為一個獨立工具,理論可以配置在所有打包器中。

babel-loader

babel作為一個獨立的工具,那麼肯定不能直接配置在webpack中。

那麼想要babel執行在webpack,就必須提供一個介面卡,來橋接兩個庫。

而這個介面卡就是babel-loader

babel-loaderwebpack執行時攔截需要轉換檔案,將檔案先交給babel進行轉換,然後再傳回webpack執行接下來的操作。

babel-loader只是呼叫了@babel/core庫中的API。最後執行的還是@babel/core引擎

下面先安裝babel-loader@babel/core

yarn add -D babel-loader@8.2.2 @babel/core@7.13.1

然後在webpack.config.js中配置所有的js檔案都使用babel-loader進行轉換。

{
   module:{
    rules:[
      {
        //  所有的.js檔案都走babel-loader
        test:/\.js$/,
        include: path.join(config.root,'src'),
        loader: "babel-loader",
      }
    ]
  },
}

? babel@6.X版本時,核心引擎庫名為babel-core。從babel@7.X版本之後,官方對庫名稱做了統一的修改,官方提供的包都以@babel/冠名,所以babel-core@babel/core實際上是一個庫 。有興趣朋友可以在NPM中對比下兩個包的版本 :@babel/corebabel-core

?後面會陸續加入其它檔案執行babel-loader。例如:.ts.jsx

但是目前依然無法轉換ES6(ES2015+)程式碼。因為只新增了引擎(@babel/core),並沒有新增具體轉換庫。

@babel/preset-env

先來介紹一下@babel/preset-env庫,來完成部分轉換功能。

@babel/preset-envbabel 預設的一個plugin

yarn add -D @babel/preset-env@7.13.5

在配置loader時,可以設定當前loader使用的屬性和依賴庫。babel-loader具有一個presets屬性來依賴的預設外掛(preset)

{
    module:{
        rules:[
            {
                //  所有的.js檔案都走babel-loader
                test:/\.js$/,
                include: path.join(config.root,'src'),
                loader: "babel-loader",
                options: {
                    presets:[
                        "@babel/preset-env",
                    ]
                }
            }
        ]
    }  
}

?? presets的執行是從後往前執行的,官方說為了確保向後相容

? presets配置可以設定短名稱,

  1. preset庫名稱以 babel-preset- 字首,可以省去字首。 例如:babel-preset-my-custom,可以直接設定為:custom
  2. 短名稱也適用於冠名,例如:@org/preset-env,可以設定為:@org/env

此時執行yarn build操作後生成的程式碼就會處理部分ES6(ES2015+)

從零學腳手架(四)---babel

生成程式碼中可以看到:awaitfor-ofconst 這些ES6程式碼被轉換了。

? 程式碼中的那堆 case 語句,是await ES5的寫法。await 本質只是一個 將非同步同步化狀態機。不熟悉 await 機制的朋友可以忽略,只需知道程式碼為await語法ES5寫法即可。

但細心的朋友可以發現,並不是所有的ES6特性被轉換了。

還有部分ES6特性並沒有被轉換(promiseincludesfilter),並且程式碼被一個箭頭函式包裹著。

程式碼被箭頭函式包裹這個問題稍後在解決。

先來了解下為什麼有的ES6特性沒有被轉換。

? @babel/preset-env取代了preset-es20系列的預設外掛(preset)

目前生成程式碼還無法在瀏覽器執行,缺少regeneratorRuntime,這個稍後再說

從零學腳手架(四)---babel

Syntax和API

思考一個問題:剛才被轉換ES6特性與未被轉換ES6特性有何不同。

答案是被轉換ES6特性是Syntax(語法),而未被轉換的則是:API(型別、函式)

babel處理ES6特性時將Syntax(語法)API(型別、函式)進行了分開處理。

為什麼要這樣做呢?

原因是兩者本質的不同:Syntax(語法)一個語言本身客觀存在的事實,而API(型別、函式),則只是對一系列操作的封裝

執行環境不支援Syntax(語法)時,那麼就只能使用其它Syntax(語法)進行替換。

執行環境不存在API(型別、函式)時,可以編寫自定義API(型別、函式)進行替換。

? JSSyntax(語法)錯誤提示是:Uncaught SyntaxErrorAPI(型別、函式)錯誤提示是:Uncaught ReferenceError

@babel/preset-env只是babel提供處理Syntax(語法)預設外掛(preset)

至於API(型別、函式)的處理,則是由其它外掛處理,這個外掛俗稱:墊片、膩子

babel配置形式

在處理API(型別、函式)之前,先介紹下babel配置檔案。

剛才在配置@babel/preset-env時,直接配置在了babel-loaderpresets屬性。

除了babel-loaderbabel還支援其它方式配置

package.json

@babel/core支援在package.json檔案設定

package.json檔案babel屬性設定babel 外掛

@babel/core執行時會嘗試讀取此屬性。

 "babel": {
   "presets": [
     "@babel/preset-env"
   ],
   "plugins": [
   ]
 }
配置檔案

babel支援使用配置檔案設定。

這種方式與webpack.config.js檔案一樣,使用.約定檔名稱設定。@babel/core執行時會嘗試讀取.約定檔案

約定檔名稱 可以為 babel.config.js.babelrc.json 。 較為常用的是 .babelrc.json 。不過一般都會省略字尾, 名稱叫做 .babelrc

從零學腳手架(四)---babel

package.json形式和配置檔案形式 只能選擇一種形式設定。如果同時存在會直接報錯。

從零學腳手架(四)---babel

babel-loader配置方式優先順序高於其他兩種方式

引數設定

在使用plugin/preset時,可以設定屬性。

不過引數形式有些奇葩。

plugin/preset與引數存在於一個陣列內,第一個為plugin/preset,第二個為屬性物件

{
  "presets": [
     ["@babel/preset-env", {
          "targets": "defaults"
     }]
  ],
  "plugins": [
  ]
}

??? 以下會使用配置檔案方式,所以一定要把babel-loader中的設定刪除掉。否則會因為優先順序問題而失效。:我就因為這個疏漏曾經被耽誤了一天

轉換API(型別、函式)

設定低版本瀏覽器

在轉換API(型別、函式)時要進行測試。

而開發人員基本上使用的都是新版瀏覽器,所以需要具有一個不支援ES6API(型別、函式)的瀏覽器。

一般ES6的新特性,都已經不再支援IE瀏覽器了。所以IE瀏覽器是一個天然的測試物件。

例如ES6Promise型別,就不再支援IE瀏覽器

從零學腳手架(四)---babel

win 10系統攜帶的IE瀏覽器版本一般都為IE11IE瀏覽器支援對版本進行修改IE瀏覽器

F12-開發者模式--模擬--文件模式 可以修改IE瀏覽器版本,在這裡使用的版本為IE9

從零學腳手架(四)---babel
處理箭頭函式包裹

在剛才打包編譯時,發現生成的程式碼使用了一個箭頭函式包裹。

這個箭頭函式函式懷疑是打包時webpack搞得鬼,具體原因沒排查,在這裡只介紹下處理方案。

package.json檔案中新增browserslist屬性,設定打包程式碼支援IE9瀏覽器。

"browserslist": [
 "ie 9"
]

? browserslist屬性是browserslist庫提供的一個屬性,browserslist是提供瀏覽器版本支援的庫。多個庫中都依賴了browserslistbrowserslist庫詳情在下一篇介紹。

此時使用yarn build執行打包編譯,生成程式碼就不再由箭頭函式包裹

從零學腳手架(四)---babel
regenerator-runtime和core-js
regenerator-runtime

介紹下關於之前打包程式碼缺少 regeneratorRuntime() 問題。

regeneratorRuntime() 是由regenerator-runtime庫提供的,

regenerator-runtime庫是一個轉換ES6generator函式await函式 功能的庫。babel直接使用此庫處理兩種函式。

core-js

很多文章介紹時regenerator-runtime都與core-js一起介紹。所以在此也將這兩個庫放在一起介紹。

處理ES6 API(型別、函式)的解決方案在上面介紹過。

執行環境不存在API(型別、函式)時,可以使用自定義API(型別、函式)進行替代。

core-js庫就是一個自定義的API(型別、函式)庫。也就是俗稱的膩子

core-js是 個人開源專案,並不屬於任何公司。

babel直接使用了core-js進行處理API(型別、函式)

core-js截至到編寫文章時的最新版本為@3.9.0

core-js@3.X@2.X兩個大版本間具有巨大的差異性,以至於影響到了babel。不過目前基本都是使用core-js@3.X版本。

? core-js開發者目前在開發core-js@4.X版本。可能到時候配置又會具有大變化。

@babel/polyfill

關於babel的文章中,有很多都會介紹@babel/polyfill

@babel/polyfill庫其實就是babelcore-jsregenerator-runtime的封裝庫。

不過在babel官網,這個庫已經被棄用了。babel@7.4.0版本之後就建議直接使用core-jsregenerator-runtime

從零學腳手架(四)---babel

上面那段話的大致意思為:@babel@7.4.0開始,@babel/polyfill會被棄用,直接使用core-jsregenerator-runtime

下面那段話的大致意思為:babel具有一個polyfill包含了core-jsregenerator-runtime

??? 關於@babel/polyfill庫被棄用的原因好像是因為:core-js@3.X版本和core-js@2.X版本的巨大差異 導致@babel/polyfill無法過渡適配

core-js、regenerator-runtime使用

yarn add regenerator-runtime@0.13.7 core-js@3.9.0 // 安裝在dependencies

直接使用core-jsregenerator-runtime需要在程式碼中手動引用。babel當然也支援配置,慢慢來

index.js檔案引用。

從零學腳手架(四)---babel

??

  1. 匯入core-js庫時,匯入方式為:"core-js/stable",是為了只匯入穩定版本特性, 關於stage請參考:[ECMAScript] TC39 process
  2. 匯入regenerator-runtime時,匯入方式為:regenerator-runtime/runtime,為了節省檔案大小

此時執行yarn build打包 編譯生成程式碼中會看到好多引用程式碼。這些都是core-js處理ES6 API(型別、函式)墊片

從零學腳手架(四)---babel

例如promise型別,就可以在編譯生成後的程式碼中找到core-js自定義的實現方式。

從零學腳手架(四)---babel

這時候使用IE9執行程式碼可以執行成功,也就是說ES6 API(型別、函式)被成功替代了。

從零學腳手架(四)---babel

@babel/preset-env 屬性設定

按需載入

剛才加入core-jsregenerator-runtime後打包執行,可以知道ES6 API(型別、函式)被成功替代了。

但其實這裡還具有一個非常嚴重的問題,那就是檔案大小。

從零學腳手架(四)---babel

可以看到打包生成的檔案現在高達428K。雖然打包程式碼壓縮,但也不應該這個大小

在程式碼中僅寫了兩個函式。那麼原因大概是引入core-jsregenerator-runtime導致。

core-jsES6 API(型別、函式)的墊片。

core-js本身並不知道你使用哪些ES6 API(型別、函式),而babel預設情況會將所有的墊片引入,

也就造成了這個恐怖的檔案大小

前端對於檔案大小非常敏感,檔案大小直接影響到網站的載入速度。所以必須要做到按需載入墊片 (僅載入需要使用的墊片)

不同專案對瀏覽器支援版本需求不一樣。

babel處理ES6 API(型別、函式)墊片時的按需載入墊片具有三種含義

  1. 按照瀏覽器版本載入墊片
  2. 按照程式碼中使用載入墊片
  3. 按照瀏覽器版本+程式碼中使用載入墊片

?瀏覽器支援版本需求 取決於專案的使用使用者,例如有的專案只是公司管理專案,無須相容老版本瀏覽器

babel@babel/preset-env提供了兩種按需載入配置方案:按照瀏覽器版本載入(1)按照瀏覽器版本+程式碼中使用載入(3)

@babel/preset-env 屬性配置

設定瀏覽器版本(browserslist、targets)

按需載入墊片中有一個瀏覽器版本載入的含義,想要實現瀏覽器版本載入那就必須設定瀏覽器版本,

babel提供了兩種設定瀏覽器版本的方案:

browserslist

browserslist方案在剛才處理函式包裹程式碼時使用到了,設定在package.json中的browserslist屬性

"browserslist": [
    "ie 9"
]

browserslist是一個提供瀏覽器版本的一個庫,提供了多種配置規則,好多庫都使用到了browserslist,例如:babel

browserslist屬性是Array,允許設定多個瀏覽器版本。例如ie 9,便是支援IE9瀏覽器。

還可以設定範圍版本,例如大於Chrome75版本。

"browserslist": [
    "Chrome > 75"
]

在這裡只使用這兩種規則測試,browserslist會在下一篇介紹

targets

targets屬性是babel自身提供瀏覽器版本設定,配置在@babel/preset-env屬性中

targets屬性型別為 StringObject;支援browserslist格式規則。

targets屬性的優先順序高於browserslist

{
    "presets": [
        ["@babel/preset-env",{
            "targets": "chrome > 75",
        }]
    ],
    "plugins": [
    ]
}
{
    "presets": [
        ["@babel/preset-env",{
            "targets": {
                "chrome": "58",
                "ie": "11"
            }]
            ],
        "plugins": [
    ]
}

推薦使用browserslist設定,也就是package.jsonbrowserslist屬性。

因為browserslist庫已經被社群高度認可。好多庫都依賴了browserslist,使用browserslist庫可以做到:配置統一管理,利於專案維護

?:?? 瀏覽器版本設定也會影響Syntax(語法)的轉換。 指定的瀏覽器版本支援的Syntax(語法)不會被轉換ES5

corejs

在介紹按需載入墊片之前再說一個@babel/preset-env屬性:corejs

corejs屬性是babel@7.4.0時加入的,用於設定載入core-js版本。

corejs設定的型別為: StringObject

{
    "presets": [
        ["@babel/preset-env",{
            "corejs": {
                "version": "3.9",
                "proposals":true
            }
        }]
    ],
    "plugins": [
    ]
}
  • version:設定載入的core-js版本。

    此屬性可以設定任何受支援的core-js

    引數型別為 String

    預設值為:2.0

  • proposals:是否載入core-js支援的 提議API

    引數型別為:Boolean

    預設值為:false

?? corejs屬性只有在啟用按需載入墊片(useBuiltIns設定為entryusage才有效。

useBuiltIns

按需載入墊片是由@babel/preset-env庫提供的useBuiltIns屬性設定。

useBuiltIns屬性可以設定三個屬性值:

false

不啟用按需載入墊片功能,全部載入core-js墊片。此值為預設值。

entry

按照瀏覽器版本載入墊片。

{
  "presets": [
    ["@babel/preset-env",{
      "useBuiltIns": "entry",
      "corejs": {
        "version": "3.9",
        "proposals":true
      }

    }]
  ],
"plugins": [
  ]
}

browserslist屬性為 Chrome > 75 時 打包出來的檔案大小就會小很多

"browserslist": [
    "Chrome > 75"
]
從零學腳手架(四)---babel

可以看到,此時檔案大小與剛才是天壤之別。因為瀏覽器設定的為 Chrome > 75 ,幾乎支援全部新特性

從零學腳手架(四)---babel

可以看到打包生成程式碼中沒有提供filter墊片,並且 await 語法都沒有轉換。這些特性在新版Chrome都提供了。

如果將browserslist屬性設定為 ie 9

那麼檔案大小依然會很大。因為ES6 新特性都不支援IE 9

"browserslist": [
    "ie 9"
]
從零學腳手架(四)---babel

usage

剛才使用entry屬性值實現了按照瀏覽器版本載入墊片的功能。

不過並不能算是我們需要的真正按需載入墊片。

useBuiltIns屬性的usage值提供了理論上真正的按需載入瀏覽器版本+程式碼中使用

{
  "presets": [
    	["@babel/preset-env",{
      "useBuiltIns": "usage",
      "corejs": {
        "version": "3.9",
        "proposals":true
      }
 	}]
  ],
  "plugins": [
  ]
}

在使用usage屬性值時,就不需要手動引用core-jsregenerator-runtime庫了

babel會自動載入。

從零學腳手架(四)---babel

此時哪怕設定ie 9。打包檔案大小也不會像entry時那麼大了。

"browserslist": [
    "ie 9"
]
從零學腳手架(四)---babel

而在Chrome > 75的情況下,程式碼都不需要進行處理了

"browserslist": [
    "Chrome > 75"
]
從零學腳手架(四)---babel

entry、usage有話說

  1. babel在處理entry屬性值時,直接將按需載入處理邏輯做到了入口。而在處理usage時,則在用到時進行了單獨引用,並且保證每一個API(型別、函式)只引用一次

  2. 在兩者選擇使用時,不要一味的追求usage,因為usage使用起來更為棘手

    從零學腳手架(四)---babel
modules

@babel/preset-env配置項中有一個modules

modules屬性表示是否將ES modules轉換為指定模組型別處理。

modules屬性值具有:amdsystemjsumdcommonjscjsautofalse

預設屬性值為auto:預設情況下,使用ES modules來進行處理,但是會受到其它pluginmodules屬性影響。

推薦使用ES modules,將屬性值設定為false

因為ES6 modules 可以進行tree-shaking優化

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules":false
      }
    ]
  ]
}

@babel/preset-env還有一些別的屬性,在此就不贅述。有興趣的朋友可以查詢官網

@babel/plugin-transform-runtime

babel處理ES6特性時,還提供了一個解決全域性汙染的墊片庫:@babel/plugin-transform-runtime

@babel/plugin-transform-runtime也是一個經常被使用到的庫。

在日常開發中都應該遵守的一個原則:避免全域性汙染

全域性汙染是一件極為可怕的問題。在協同、程式碼執行時會出現不可預知的問題。

@babel/plugin-transform-runtime庫就是將程式碼使用到的ES6 API(型別、函式)名稱轉換為自定義名稱,從而避免汙染執行環境自身API

? @babel/plugin-transform-runtimeusage屬性值一樣:按照瀏覽器版本+程式碼中使用載入墊片

開發第三方庫,強烈建議使用@babel/plugin-transform-runtime

@babel/runtime-corejs3

@babel/plugin-transform-runtime庫依賴了一個@babel/runtime-corejs3@babel/runtime-corejs2庫。

???

@babel/runtime-corejs3對應的core-js@3.X

@babel/runtime-corejs2對應的core-js@2.X

@babel/runtime-corejs3babel提供的core-js封裝庫,內部做了一些處理,具體可以參考這篇文章。不過此文章是基於@babel/runtime-corejs2版本,與@babel/runtime-corejs3具有一定差異。

yarn add -D @babel/plugin-transform-runtime@7.13.7 @babel/runtime-corejs3@7.13.7

?? 使用@babel/plugin-transform-runtime時,就不需要安裝core-jsregenerator-runtime@babel/runtime-corejs3中會依賴這兩個庫

.babelrc檔案中使用@babel/plugin-transform-runtime配置替代@babel/preset-env中配置。

不過注意的是@babel/plugin-transform-runtime屬性中corejs.version不再是字串,而是23。 因為載入的是@babel/runtime-corejs[3/2]

{
  "presets": [
    [
      "@babel/preset-env",
      {
        //  移除useBuiltIns設定
        //      "targets": "chrome > 75",
        //      "useBuiltIns": "usage",
        //      "corejs": {
        //        "version": "3.9",
        //        "proposals":true
        //      }
      }
    ]
  ],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": {
          "version": 3,
          "proposals": true
        }
      }
    ]
  ]
}

配置完畢後,不再需要任何引用就可以進行打包生成。

"browserslist": [
    "ie 9"
]

IE9環境yarn build

可以看到使用的ES6-API已經被轉換為另外的API了,所以並不會再汙染全域性程式碼。至於打包的大小,並沒有多大

從零學腳手架(四)---babel 從零學腳手架(四)---babel

至於在Chrome > 75的打包結果,有興趣的朋友可以自行測試。

preset和plugin

在使用babel庫時,發現有兩種型別:

  1. preset@babel/preset-env
  2. plugin@babel/plugin-transform-runtime

配置時也是不同屬性:

{
  "presets": [
    
  ],
  "plugins": [
  ]
}

preset的中文翻譯為:預置。其實也就是babel提供的預置外掛庫,其本質也都是plugin

從零學腳手架(四)---babel

總結

???

  • babel來用來處理ES6特性的庫
  • babel也是核心引擎 + 外掛化的設計模式
  • babel-loaderbabel的介面卡,將babel提供webpack使用
  • babel使用不同的外掛分別處理Syntax(語法)API(型別、函式)
  • babel提供不少的預設外掛,配置在presets屬性中。
  • @babel/preset-envuseBuiltIns屬性用來設定按需載入墊片
  • @babel/plugin-transform-runtime提供了一種不汙染全域性情況下使用墊片方式。

本文參考

本文依賴

package.json

{
  "name": "my-cli",
  "version": "1.0.0",
  "main": "index.js",
  "author": "mowenjinzhao<yanzhangshuai@126.com>",
  "license": "MIT",
  "devDependencies": {
    "@babel/core": "7.13.1",
    "@babel/plugin-transform-runtime": "7.13.7",
    "@babel/preset-env": "7.13.5",
    "@babel/runtime-corejs3": "7.13.7",
    "babel-loader": "8.2.2",
    "clean-webpack-plugin": "3.0.0",
    "html-webpack-plugin": "5.2.0",
    "webpack": "5.24.0",
    "webpack-cli": "4.5.0"
  },
  "dependencies": {
    "jquery": "3.5.1",
  },
  "scripts": {
    "start": "webpack --mode=development  --config webpack.config.js",
    "build": "webpack --mode=production  --config webpack.config.js"
  },
  
  "browserslist": [
    "ie 9",
    "Chrome > 75"
    ]
}

webpack.config.js

const path = require('path')
const webpack = require("webpack");
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')


const config = {
  root: path.join(__dirname, './'),
}

const modules = {

  //  入口檔案
  //  字串形式
  entry: path.join(config.root, 'src/index.js'),
  //  物件形式
  // entry:{
  //   'index':  path.join(config.root, 'src/index.js'),
  // },

  //  輸出檔案
  //  字串形式
  // output:path.join(config.root, './dist/[name].js')
  //物件形式
  output: {
    //  輸出檔案的目錄地址
    path: path.join(config.root, 'dist'),
    //  輸出檔名稱,contenthash代表一種快取,只有檔案更改才會更新hash值,重新打包
    filename: '[name]_[contenthash].js'
  },

  //devtool:false, //'eval'

  module:{
    rules:[
      {
        //  所有的.js檔案都走babel-loader
        test:/\.js$/,
        include: path.join(config.root,'src'),
        loader: "babel-loader"
      }
    ]
  },


  optimization: {
    minimize: false,
    minimizer: [
    new TerserPlugin({
          //  指定壓縮的檔案
          include: /\.js(\?.*)?$/i,

          // 排除壓縮的檔案
          // exclude:/\.js(\?.*)?$/i,

          //  是否啟用多執行緒執行,預設為true,開啟,預設併發數量為os.cpus()-1
          //  可以設定為false(不使用多執行緒)或者數值(併發數量)
          parallel: true,

          //  可以設定一個function,使用其它壓縮外掛覆蓋預設的壓縮外掛,預設為undefined,
          minify: undefined,

          //  是否將程式碼註釋提取到一個單獨的檔案。
          //  屬性值:Boolean | String | RegExp | Function<(node, comment) -> Boolean|Object> | Object
          //  預設為true, 只提取/^\**!|@preserve|@license|@cc_on/i註釋
          //  感覺沒什麼特殊情況直接設定為false即可
          extractComments: false,

          // 壓縮時的選項設定
          terserOptions: {
            //  是否保留原始函式名稱,true代表保留,false即保留
            //  此屬性對使用Function.prototype.name
            //  預設為false
            keep_fnames: false,

            // 是否保留原始類名稱
            keep_classnames: false,

            //  format和output是同一個屬性值,,名稱不一致,output不建議使用了,被放棄
            // 指定壓縮格式。例如是否保留*註釋*,是否始終為*if*、*for*等設定大括號。
            format: {
              comments: false,
            },
            output: undefined,

            //  是否支援IE8,預設不支援
            ie8: false,

            compress: {
              // 是否使用預設配置項,這個屬性當只啟用指定某些選項時可以設定為false
              defaults: false,

              // 是否移除無法訪問的程式碼
              dead_code: false,

              // 是否優化只使用一次的變數
              collapse_vars: true,

              warnings: true,

              //  是否刪除所有 console.*語句,預設為false,這個可以線上上設定為true
              drop_console: false,

              //  是否刪除所有debugger語句,預設為true
              drop_debugger: true,

              //  移除指定func,這個屬性假定函式沒有任何副作用,可以使用此屬性移除所有指定func
              // pure_funcs: ['console.log'], //移除console
            },
          },
    	})
    ]
  },

  plugins: [
    new HtmlWebpackPlugin({
       //  HTML的標題,
        //  template的title優先順序大於當前資料
        title: 'my-cli',

        //  輸出的html檔名稱
        filename: 'index.html',

        //  本地HTML模板檔案地址
        template: path.join(config.root, 'src/index.html'),

        // 引用JS檔案的目錄路徑
        publicPath: './',

        //  引用JS檔案的位置
        //  true或者body將打包後的js指令碼放入body元素下,head則將指令碼放到中
        //  預設為true
        inject: 'body',

        //  載入js方式,值為defer/blocking
        //  預設為blocking, 如果設定了defer,則在js引用標籤上加上此屬性,進行非同步載入
        scriptLoading: 'blocking',

        //  是否進行快取,預設為true,在開發環境可以設定成false
        cache: false,

        //  新增mate屬性
        meta: {}
    }),

    new CleanWebpackPlugin({
 		// 是否假裝刪除檔案
        //  如果為false則代表真實刪除,如果為true,則代表不刪除
        dry: false,

        //  是否將刪除日誌列印到控制檯 預設為false
        verbose: true,

        //  允許保留本次打包的檔案
        //  true為允許,false為不允許,保留本次打包結果,也就是會刪除本次打包的檔案
        //  預設為true
        protectWebpackAssets: true,

        //  每次打包之前刪除匹配的檔案
        cleanOnceBeforeBuildPatterns: ['**/*'],

        //  每次打包之後刪除匹配的檔案
        cleanAfterEveryBuildPatterns:["*.js"],
    }),


    new webpack.DefinePlugin({ "global_a": JSON.stringify("我是一個打包配置的全域性變數") }),
  ],

  resolve: {
    alias:{
      //  設定路徑別名
      '@': path.join(config.root, 'src') ,

      '~':  path.join(config.root, './src/assets') ,
    },
    //  可互忽略的字尾
    extensions:['.js', '.json'],
    //  預設讀取的檔名
    mainFiles:['index', 'main'],
  }
}

//  使用node.js的匯出,將配置進行匯出
module.exports = modules

.babelrc

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules":false
        //  移除useBuiltIns設定
        //      "targets": "chrome > 75",
        //      "useBuiltIns": "usage",
        //      "corejs": {
        //        "version": 3,
        //        "proposals":true
        //      }
      }
    ]
  ],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": {
          "version": 3,
          "proposals": true
        }
      }
    ]
  ]
}

相關文章