從零學腳手架(三)—webpack屬性詳解

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

在上一篇中,介紹了webpackentryoutputplugins屬性。

在這一篇,接著介紹其它配置屬性。

mode

這個屬性在上一篇中使用過一次,設置webpack編譯模式的,那麼這個屬性到底是什麼東西呢?

打包器是將開發環境程式碼編譯可部署環境程式碼

搭建的工程化程式碼基本都無法直接運行在瀏覽器,所以本地測試也都是使用打包編譯後的程式碼預覽。

但是本地開發預覽又必須具有程式碼可讀性、可調試性等條件。

webpack為了解決這個問題,就提供了兩種打包模式開發模式(development)發布模式(production)

mode屬性設置

 {
   mode:'development'
 }

也可以使用CLI參數進行設置

🐋🐋

  • webpack默認使用的是發布模式(production),缺少mode屬性執行時,webpack會進行提示
  • CLI參數設置 優先順序要高於 webpack.config.js 文件設置。
  • 更好的方案是使用兩個webpack.config.js文件,腳手架一般都是這樣區分,在之後介紹webpack merge時處理

測試兩種模式的區別時,最直觀區別就是查看編譯生成的程式碼是否進行了壓縮:在production模式下,webpack會預設壓縮plugin

webpack中的mode屬性值其實具有三個:developmentproductionnone

none屬性值與兩者的區別只是沒有預設任何插件

🐋🐋🐋 developmentproduction兩種模式只是針對不同場景下功能差異化的區分,其實現具體的功能還是使用的插件

為了配置簡單化,developmentproduction兩種模式都預設了一些基本插件

下面來介紹下 developmentproduction兩種模式中的預設的部分功能

development

process.env.NODE_ENV

development模式時,webpack使用內置DefinePlugin

預設了一個環境變數屬性process.env.NODE_ENV,屬性值為development

開發人員可以編寫業務程式碼時根據process.env.NODE_ENV屬性判斷當前編譯模式,以此執行不同環境中的程式碼。

process.env.NODE_ENV屬性和DefinePlugin稍後詳細介紹

設置模組和模組名稱設置有效性

development模式時,webpack會將JS模組模組名稱設置為有效名稱,用來方便調試

[email protected]版本設置程式碼可讀性使用的是webpack內置的pluginNamedModulesPluginNamedChunksPlugin

[email protected]版本設置程式碼可讀性使用的是 optimization.moduleIdsoptimization.chunkIds 兩個屬性。

但根源也是使用內置pluginNamedModuleIdsPluginNamedChunkIdsPlugin

optimization: {
    moduleIds: 'named',
    chunkIds: 'named',
}

optimization屬性是webpack提供的優化屬性,與mode一樣,只是為了方便管理,其根源還是使用插件設置的。

設置devtool屬性

development模式時,webpack會將devtool屬性設置為eval

devtool屬性是控制SourceMap文件如何生成的。SourceMap是用於將原始模組文件與打包後的程式碼映射文件。用於調試使用。具體稍候介紹

production

process.env.NODE_ENV

production模式時,webpack使用內置DefinePlugin

預設一個環境變數屬性process.env.NODE_ENV,屬性值為production

開發人員可以編寫業務程式碼時根據process.env.NODE_ENV屬性判斷當前編譯模式,以此執行不同環境中的程式碼。

process.env.NODE_ENV屬性和DefinePlugin稍後詳細介紹

設置模組和模組名稱混淆

production模式時,webpackJS模組模組名稱進行混淆,以保證程式碼安全性

[email protected]版本設置程式碼可讀性使用的是webpack內置的pluginNamedModulesPluginNamedChunksPlugin

[email protected]版本設置程式碼可讀性使用的是 optimization.moduleIdsoptimization.chunkIds 兩個屬性。

但根源也是使用內置pluginDeterministicModuleIdsPluginDeterministicChunkIdsPlugin

optimization: {
    moduleIds: 'deterministic',
    chunkIds: 'deterministic',
}
程式碼壓縮

production模式時,webpack 開啟了程式碼壓縮優化 ,使用terser-webpack-plugin庫對打包生成程式碼進行壓縮

🐋🐋 [email protected]默認使用terser-webpack-plugin壓縮程式碼,[email protected]版本及之前版本,默認使用的壓縮庫為uglifyjs-webpack-plugin。但uglifyjs-webpack-plugin已停止維護

作用域提升

production模式時,webpack 會使用內置的ModuleConcatenationPlugin 對程式碼的作用域進行提示。用於減少打包生成的程式碼量和執行速度。

錯誤處理

production模式時,webpack 會預設內置NoEmitOnErrorsPlugin

打包編譯時,如果出現程式碼錯誤,則不在生成程式碼。用於避免程式碼錯誤程式碼依然打包成功

🐋🐋[email protected][email protected]對於developmentproduction 預設功能具有一定的差異,具體請參考 webpack5-modewebpack4-mode顯微鏡下的webpack4的新特性:mode詳解

DefinePlugin

developmentproduction兩種模式中, 都設置了一個環境變數屬性:process.env.NODE_ENV,只是屬性值不相同。

環境變數用於編寫業務程式碼時 針對不同環境下的差異化程式碼。例如調用第三方SDK時:區分開發環境正式環境

當然可以選擇每次發版時手動修改配置,只要自己不會覺得麻煩。

做一個測試

/src/index.js中輸出process.env.NODE_ENV屬性

在執行yarn start後查看打包生成程式碼會看到process.env.NODE_ENV 替換為了development字元串

同樣如果執行yarn build process.env.NODE_ENV 屬性 會替換成production字元串

這就是 process.env.NODE_ENV環境變數的作用,webpack 在打包編譯時會將設置的環境變數屬性值進行替換,可以在編寫業務程式碼時進行環境判斷。

webpack 使用了內置的DefinePlugin設置 process.env.NODE_ENV

當然也可以使用DefinePlugin設置自定義環境變數。具體詳情請參考:官網

const webpack = require("webpack");
{
    plugins:[
         new webpack.DefinePlugin({ "global_a": JSON.stringify("我是一個打包配置的全局變數") }),
    ]
}

devtool

development模式中會設置devtool屬性。

devtool屬性也是webpack提供的一個屬性項。用於設置javascript-source-map

我們都看過打包編譯生成的程式碼,哪怕是development模式下生成的,也是超級混亂。

而想要對這些程式碼調試排查錯誤,那簡直是個噩夢。

對於這個問題,Google提供了一種工具叫做:javascript-source-map

javascript-source-map提供一個映射資訊,將 打包編譯生成的程式碼開發編寫的程式碼文件 進行映射,調試時直接針對 開發編寫的程式碼文件進行調試。

🐋 source-map詳細介紹請參考阮一峰老師的:JavaScript Source Map 詳解

webpack提供了devtool屬性來設置javascript-source-map

development模式 devtool屬性默認值為 eval

production模式 devtool屬性默認值為 false(none)

eval屬性值生成的程式碼都是由eval語法編譯,並提供了一個sourceURL屬性用於指向文件源路徑

devtool屬性具有非常多的屬性值,不同的屬性值 操作具有差異 和 打包消耗時間不同。

有的屬性值會生成一個.map文件,個文件中存放映射資訊,有的直接在生成文件中顯示映射資訊。

在此就不不詳細介紹devtool,有興趣的朋友可以參考官網自行測試

{
	//	屬性可以設置為false和字元串
	devtool:false; // 'eval'
}

optimization

webpack針對程式碼優化管理,提供了optimization屬性進行管理。

就像剛才介紹的optimization.moduleIdsoptimization.chunkIds提供了對模組模組名稱管理。

但其根源還是使用了插件進行管理,屬性只是為了方便管理。

optimization對象具有好多屬性,在此也不詳細介紹,

只介紹optimization.minimizeoptimization.minimizer。這兩個也是經常被使用到屬性。

minimize和minimizer

minimize

先來做一個測試,將optimization.minimize手動改為false

  optimization:{
    minimize:false
  }

此時使用yarn build執行打包可以看到程式碼並沒有進行壓縮

也就是optimization.minimize屬性是控制程式碼壓縮的。

production模式只是將optimization.minimize設置為了true

  optimization:{
    // 開啟默認優化
    minimize:true
  }
minimizer

optimization對象中還具有一個minimizer屬性,這個屬性和plugins屬性功能相同,都是用來設置plugin的。

而兩者的區別在於:optimization.minimizer會受到optimization.minimize屬性的管理

optimization.minimizer屬性會受到optimization.minimize屬性的控制:

如果optimization.minimize屬性值為false,那麼就不載入設置在optimization.minimizer屬性中的plugin

也就是optimization.minimize是控制optimization.minimizer屬性的開關。

terser-webpack-plugin 默認情況下是設置在optimization.minimizer屬性中,所以optimization.minimize屬性設置為false 程式碼會不壓縮。

如果terser-webpack-plugin 手動設置在plugins屬性中,

那麼就算 optimization.minimizefalse,程式碼依然會壓縮。

🐋 webpack已經依賴了terser-webpack-plugin,所以就不需要再安裝

🐋 [email protected]以下默認使用的壓縮褲為uglifyjs-webpack-plugin,好多文章都是以uglifyjs-webpack-plugin為基礎講解的,不過uglifyjs-webpack-plugin目前不再維護,[email protected]開始改為了terser-webpack-plugin

optimization.minimizeoptimization.minimizerwebpack為方便管理提供的屬性。

在配置時可以將關於優化的plugin設置在optimization.minimizer屬性,由optimization.minimize統一管理。

webpack提供的一系列默認值提供了最小配置。

代價卻提高了webpack學習成本。讓很多人對這些屬性感到迷惑。

terser-webpack-plugin

terser-webpack-plugin 作為[email protected]默認的壓縮工具。在此就直接介紹此庫的屬性

🐋🐋 terser-webpack-plugin壓縮對devtool屬性具有一定的要求,只支援nonesource-mapinline-source-maphidden-source-mapnosources-source-map。 像eval生成的是字元串。terser-webpack-plugin就沒辦法進行處理

在剛才手動設置terser-webpack-plugin時沒有添加任何參數。

terser-webpack-plugin是有很多配置項的,配置項通過構造函數傳遞。

terser-webpack-plugin第一層參數主要對於文件多執行緒的設置。

const TerserPlugin = require('terser-webpack-plugin');

{
    optimization: {
    // 配置可優化
    minimize: true,
    minimizer: [
      new TerserPlugin({
        //  指定壓縮的文件
        include: /\.js(\?.*)?$/i,
          
        // 排除壓縮的文件
        // exclude:/\.js(\?.*)?$/i,
          
        //  是否啟用多執行緒運行,默認為true,開啟,默認並發數量為os.cpus()-1
        //  可以設置為false(不使用多執行緒)或者數值(並發數量)
        parallel: true,

        //  可以設置一個function,使用其它壓縮插件覆蓋默認的壓縮插件,默認為undefined,d,
        minify: undefined,

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

        //  壓縮時的選項設置
        terserOptions: {}
      })
    ]
  }
}
  • include:指定壓縮的文件

    屬性可設置為:StringString[]Regex

    默認值為:undefined

  • exclude:排除壓縮的文件

    屬性可設置為:StringString[]Regex

    默認值為:undefined

  • parallel:是否啟用多執行緒運行

    屬性可設置為:BooleanNumber

    屬性值為false:不啟動多執行緒

    屬性值為true:啟動多執行緒,多執行緒數量為:os.cpus()-1

    屬性值為Number:表示使用的多執行緒數量

    默認值為:true

  • minify:設置其它壓縮工具覆蓋terser-webpack-plugin

    此屬性可以設置一個函數,函數內允許使用其它壓縮工具替代terser-webpack-plugin, 其實相當於做了一個攔截,基本上不會使用此屬性。 詳細介紹可以參考 官方

    屬性可設置為:Function

    默認值為undefined

  • extractComments:是否將程式碼注釋提取到一個單獨的文件。

    經過壓縮的程式碼都會去除注釋,此屬性就是設置是否提取注釋,個人感覺這個屬性也沒什麼用。詳細介紹可以參考 官方

    屬性可設置為:BooleanStringRegExpFunction<(node, comment) -> Boolean | Object>Object

    屬性值為false或者函數返回false:表示不提取

    屬性值為String時: all表示全部提取。some表示使用默認正則匹配:/^**!|@preserve|@license|@cc_on/i

    屬性值為true或者函數返回true時:表示提取,使用默認正則匹配:/^**!|@preserve|@license|@cc_on/i

    屬性值為Regex時:自定義提取規則。

    屬性值為Object時:允許自定義提取條件。

    默認值為true

  • terserOptions:設置壓縮選項

    此屬性才是詳細設置壓縮選項的參數。

    屬性可設置為:Object

terserOptions屬性

先來做一個測試,在index.js中創建這麼一個函數

使用默認壓縮配置進行打包編譯,結果可以看到生成的程式碼只有真實執行的程式碼。

默認terser-webpack-plugin配置基本上做到了最優解。

🐋 如果將var a = 1改為let a = 1,則結果有些不一致,這是由於ES6問題,有興趣的諸君可以測試一下

terser-webpack-plugin配置屬性中:terserOptions.compress屬性才是控制壓縮。

terserOptions.compress 設置類型為 BooleanObject

接下來將此屬性設置為false。查看打包編譯程式碼,可以發現,程式碼並沒有被壓縮,只是改變了屬性名稱和函數函數

{
   optimization: {
    // 配置可優化
    minimize: true,
    minimizer: [
      new TerserPlugin({
        //  壓縮時的選項設置
        terserOptions: {
             compress:false
        }
      })
    ]
  }
}

terserOptions這一層中的設置主要是對程式碼中屬性名稱函數名稱。等一系列的設置

{
   optimization: {
    // 配置可優化
    minimize: true,
    minimizer: [
      new TerserPlugin({
          //  壓縮時的選項設置
          terserOptions: {
              //  是否保留原始函數名稱,true代表保留,false即保留
              //  此屬性對使用Function.prototype.name
              //  默認為false
              keep_fnames:false,
              
              // 是否保留原始類名稱
              keep_classnames:false,
            
              //  format和output是同一個屬性值,,名稱不一致,output不建議使用了,被放棄
              // 指定壓縮格式。例如是否保留*注釋*,是否始終為*if*、*for*等設置大括弧。
              format: {comments:true},
              output: undefined,
              
              //  是否支援IE8,默認不支援
              ie8:true,
              
                //  ·壓縮配置
              compress: {  },
        }
      })
    ]
  }
}
  • keep_fnames:是否保留原始函數名稱

    剛才測試看到了,默認情況下會更改函數名稱,此屬性就是設置是否保留函數名稱。

    屬性可設置為:Boolean

    屬性值為false:表示不保留原始名稱

    屬性值為true:表示保留原始名稱

    默認值為false

  • keep_classnames: 是否保留原始類名稱

    keep_fnames屬性類似,只不過設置的是類名稱

    屬性可設置為:Boolean

    屬性值為false:表示不保留原始名稱

    屬性值為true:表示保留原始名稱

    默認值為false

  • format/output:指定壓縮格式。例如是否保留注釋,是否始終為iffor等設置大括弧。

    formatoutput的配置相同。output官方不再推薦使用。這個屬性就不介紹,具體請參考官方

    屬性可設置為:Object

    默認值為null

  • ie8:是否支援IE8

    屬性可設置為:Boolean

    默認值為false

  • compress:設置壓縮選項

    屬性可設置為:BooleanObject

    屬性值為false:表示不壓縮。

    屬性值為object:自定義壓縮設置。

下面介紹下terserOptions.compress的配置。terserOptions.compress只介紹部分屬性 。其它設置,有興趣的朋友可以查看官方

{
   optimization: {
    // 配置可優化
    minimize: true,
    minimizer: [
      new TerserPlugin({
          //  壓縮時的選項設置
          terserOptions: {
                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
          	}
        }
      })
    ]
  }
}
  • defaluts:是否使用默認配置項

    此屬性表示是否使用官方設置默認配置項

    屬性可設置為:Boolean

    默認值為true

  • dead_code:是否移除無法訪問的程式碼

    屬性可設置為:Boolean

    默認值為true

  • collapse_vars:是否優化只使用一次的變數

    此屬性表示是否將只使用一次的變數直接進行替換優化

    屬性可設置為:Boolean

    默認值為true

  • drop_console:是否刪除所有console語句

    此屬性可以在發布時設置為true

    屬性可設置為:Boolean

    默認值為false

  • drop_debugger:是否刪除所有debugger語句

    屬性可設置為:Boolean

    默認值為true

  • pure_funcs:移除指定的函數。

    此屬性可以設置移除指定的函數,但是需要缺點要移除的函數沒有任何副作用(沒有使用),有興趣的朋友可以測試刪除自定義函數

terser-webpack-plugin配置項還有好多,但是一般使用默認屬性即可,

會被使用到可能也就是 :

terserOptions.compress.drop_console去除所有consoleparallel來設置多執行緒

其它一般都是默認值即可。

🐋🐋 暫時先將optimization.minimize屬性設置false,可以更方便的查看生成程式碼

loader

在上一篇文章說過:webpack是一個JavaScript應用程式的靜態模組打包器,其本身並不支援非JS模組

webpack提供了將 非JS模組 轉換為JS模組的功能—loader

loader相當於一個攔截器,將指定文件進行編譯為JS模組,再傳遞給webpack

在這裡先不學習具體的loader ,只介紹下loader 的配置語法。

loader的配置是在module.rules屬性,module.rules是一個Array類型屬性。

數組每一項都可以設置 攔截文件使用指定loader

{
  module:{
    rules:[
      {
        // test:/\.css$/,
        // include:path.join(__dirname,'src'),
        // exclude:path.join(__dirname,'node_modules'),
        // //  字元串形式
        // use:'css-loader',
        //  數組形式,可以設置多個loader
        // use:[
        //   {
        //     loader:'css-loader',
        //     options:{
        //
        //     }
        //   }
        // ]
      }
    ]
  }
}
  • test:設置攔截文件

    使用此屬性設置攔截的文件。例如:/.css$ 表示攔截所有的.css文件。使用Regex可以攔截多種文件類型使用同一loader

    屬性可設置為:Regex

  • include:包含攔截的文件目錄。

    此屬性可以設置攔截指定目錄的文件,一般使用此屬性設置只攔截/src目錄中文件

    屬性可設置為:String

  • exclude:排除攔截的文件目錄。

    此屬性與include類似,只不過功能相反,指定要排除的目錄。一般使用此屬性排除node_modules目錄。

    此屬性與include只使用一種。

    屬性可設置為:String

  • use:攔截到的文件所使用的loader

    屬性可設置為:StringArray

    屬性值為String:設置loader 名稱

    屬性值為Array:可以指定多個loader 處理,並且可以對每一個loader 設置屬性配置。

🐋🐋🐋 當指定多個loader 時,loader 載入順序為從右往左。具體請參考Webpack的Loader為什麼是從右往左寫

resolve

resolvewebpack提供的一個屬性,用來配置打包編譯時的模組解析規則。

resolve是一個Object類型,具有不少的屬性配置。

在此只介紹三個常用的屬性。其它屬性,有興趣的朋友可以去參考中文官網

alias

使用vue-cli這類腳手架,開發時引入本地文件模組,通常可以使用一個符號(@)來代替/src工作目錄。

這個功能就是resolve.alias提供的。

resolve.alias屬性可以對一個指定路徑設置別名。打包編譯時會將設置別名替換為配置的真實路徑

{
   resolve: {
    alias:{
      //  設置路徑別名
      '@':path.join(__dirname,'src'),
      '~': path.resolve(__dirname, '../src/assets')
    },
  }
}

此時在引用文件模組時,就可以使用@來代替/src工作目錄(工作根目錄)


🐋 webpack允許設置除關鍵字外的任意符號作為別名。vue-cli這類腳手架一般都預設符號(@)代替/src工作目錄。

extensions

使用vue-cli這類腳手架,開發時引入本地文件模組。很常見的一種行為就是不需要添加後綴名稱。

這個功能就是resolve.extensions提供的。

resolve.extensions功能允許設置多個後綴名稱。導入文件模組時可以忽略文件後綴名稱,

打包編譯時按照配置綴順序依次匹配,直到尋到第一個匹配文件或報錯(找不到匹配文件)。

resolve.extensions屬性類型為Array,默認值為:[‘.js’, ‘.json’],也就是可以忽略JS文件和JSON文件後綴

下面將resolve.extensions設置為[‘.json’]做一個測試

{
   resolve: {
    extensions:['.json']
  }
}

此時由於引用index2.js時還是沒有添加後綴,打包編譯時就直接報錯了。

而引用index2.js添加.js後綴名稱才可以打包成功。。

vue-clireact-cli這類腳手架都會在resolve.extensions屬性配置自己文件類型的後綴名稱。

在此以react-cli為例


🐋🐋🐋 打包編譯時匹配resolve.extensions,使用的是隊列形式。Array從先到後

mainFiles

使用vue-cli這類腳手架,開發時引入本地文件模組,有一種常見方式只指定其文件所在目錄,並沒有指定文件名稱。

這種方式常見於以 目錄為組件單元的程式碼風格。例如antd,就是以目錄為組件單元。

這個功能是由resolve.mainFiles屬性提供的。

resolve.mainFiles允許設置多個文件名稱,導入文件模組時可以忽略此文件名稱。

打包編譯時按照配置綴順序依次匹配,直到尋到第一個匹配文件或報錯(找不到匹配文件)。

resolve.extensions屬性類型為Array,默認值為:[‘index’],也就是可以忽略index名稱的文件。

🐋🐋 使用resolve.mainFiles時需要設置resolve.extensions,目錄是沒有後綴的,需要設置忽略後綴,否則會報錯:

下面將resolve.mainFiles設置為[‘index’,’main’]做測試

{
   resolve: {
   	extensions:['.js','.json'],
    mainFiles:['index','main'],
  }
}


可以看到, 可以看到導入/demo/main.js時,忽略了文件名稱,但是依然打包編譯、導入成功

🐋🐋🐋 打包編譯時匹配resolve.mainFiles,使用的也是隊列形式。Array從先到後

🐋🐋🐋 個人建議使用目錄結構方式組織組件

context

配置webpack時,使用文件目錄時都使用了絕對地址:path.join(__dirname, …)

webpack其實提供了一個context屬性:在配置項中允許 以此目錄為基準 設置 相對目錄。

不過context屬性個人感覺並不太好用。

context屬性String類型,設置一個絕對路徑的目錄地址

context默認值為當前項目根目錄,也就是package.json文件所在目錄

context屬性默認值為當前項目根目錄,所以可以直接使用相對路徑

{
    entry: './src/index.js' ,
}

此時執行yarn build打包也可以進行打包成功。

也可以使用context指定其它目錄為基準目錄

{
  context: path.join(__dirname, './src'),
  //  入口文件
  //  字元串形式
  entry: './index.js' ,
}

context屬性具有一定的缺陷

output不允許相對路徑

並不是所有的屬性都可以設置為相對路徑

例如output屬性就只允許使用絕對路徑。

配置屬性中既有相對路徑又有絕對路徑,對於我這個強迫症來說感覺怪怪的。

🐋 output屬性只允許使用絕對路徑應該是為了保證輸出地址的準確安全性。

基準絕對路徑

個人比較喜歡的方案就是自定義一個root目錄。

在配置文件中 root目錄 去設置路徑

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

const {
  entry: path.join(config.root, 'src/index.js'),
  output: {
    path: path.join(config.root, 'dist'),
    filename: '[name]_[contenthash].js'
  },
}

至於自定義root屬性而不直接使用__dirname原因是:

個人感覺自定義屬性方便控制。 例如更換配置文件目錄,直接使用__dirname,所有目錄地址都需要更改,而自定義絕對路徑基準就只需要更改root目錄即可

當然真實開發不會出現此類情況。

使用context屬性還是絕對路徑都無傷大雅,個人習慣罷了,只要文件路徑正確即可

總結

🐋🐋🐋

  • webpack提供了兩種打包模式開發測試打包編譯(development)線上發布打包編譯(production) 。兩種打包模式能夠更加方便管理插件
  • webpackdevelopmentproduction 都預設了一些基礎功能,大大減少了開發時的配置
  • source-mapGoogle提供的打包編譯後程式碼與開發程式碼的一種映射文件,主要用途是方便開發人員調試
  • optimization屬性是webpack提供的控制優化的屬性, optimization只是一系列優化功能的集合,主要是為了方便管理,本質還是由插件完成功能
  • resolve屬性提供了打包編譯時的解析規則,

本文參考

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:[
  //     {
  //       test:/\.css$/,
  //       include: ath.join(config.root,'src'),
  //       exclude: path.join(config.root,'node_modules'),
  //       ////  字元串形式
  //       // use:'css-loader',
  //       //  數組形式,可以設置多個loader
  //       // use:[
  //       //   {
  //       //     loader:'css-loader',
  //       //     options:{
  //       //
  //       //     }
  //       //   }
  //       // ]
  //     }
  //   ]
  // }


  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

Tags: