從零到一搭建 react 項(xiàng)目系列之(十四)

前面的文章介紹了 Webpack撵割、HMR独柑、React、Redux缝其、ESLint挎塌、Prettier 等內(nèi)容。

但其實(shí) Webpack 4 部分內(nèi)容是沒有比較詳細(xì)的講述的内边,那這篇文章就來介紹它吧榴都。

編寫本文的時(shí)候,最新版本是 webpack 5.1.3漠其。而本文要介紹的時(shí)候 webpack 4.x 相關(guān)接口嘴高。

提供兩個(gè)鏈接:

請注意本文所指 Webpack 中文文檔由印記中文翻譯。

一和屎、前言

在此前的系列文章拴驮,多多少少都涉及到 webpack 的相關(guān)配置,主要有這幾項(xiàng)柴信。

今天就每一個(gè)知識點(diǎn)套啤,盡可能地都詳細(xì)介紹一下。

webpack 支持所有符合 ES5 標(biāo)準(zhǔn) 的瀏覽器(不支持 IE8 及以下版本)随常。webpack 的 import()require.ensure() 需要 Promise潜沦。如果你想要支持舊版本瀏覽器,在使用這些表達(dá)式之前绪氛,還需要提前加載 polyfill唆鸡。

{
  mode, // 模式
  entry, // 入口
  deServer, // 開發(fā)
  optimization, // 優(yōu)化
  plugins, // 插件
  resolve, // 解析
  module  // 模塊
}

即使有些內(nèi)容前面已經(jīng)介紹過,這里還是再啰嗦簡單介紹一下枣察。

安裝依賴包

不推薦全局安裝 webpack争占。這會將你項(xiàng)目中的 webpack 鎖定到指定版本,并且在使用不同的 webpack 版本的項(xiàng)目中序目,可能會導(dǎo)致構(gòu)建失敗臂痕。

$ yarn add --dev webpack@4.41.2
$ yarn add --dev webpack-cli@3.3.10
webpack 配置文件

webpack 開箱即用,可以無需使用任何配置文件宛琅。然而刻蟹,默認(rèn)情況下 webpack 會假定項(xiàng)目的入口起點(diǎn)為 src/index逗旁,然后會在 dist/main.js 輸出結(jié)果嘿辟,并且在生產(chǎn)環(huán)境開啟壓縮和優(yōu)化舆瘪。

通常,我們的項(xiàng)目還需要繼續(xù)擴(kuò)展此能力红伦,為此我們可以在項(xiàng)目根目錄下創(chuàng)建一個(gè) webpack.config.js 文件英古,webpack 會自動使用它。

Webpack 配置文件是標(biāo)準(zhǔn)的 Node.js CommonJS 模塊(所以不能使用 ESM 標(biāo)準(zhǔn)導(dǎo)出)昙读,可以導(dǎo)出為 object召调、functionPromise,本項(xiàng)目將使用導(dǎo)出 object 的形式蛮浑。雖然可行唠叛,但不建議通過 CLI 形式指定過多參數(shù),會導(dǎo)致編寫很長的腳本命令沮稚,推薦使用配置文件的形式艺沼。

使用自定義配置文件,則可通過 Webpack CLI 命令 --config 來指定蕴掏。

// package.json
// 假定項(xiàng)目根目錄下有兩個(gè)配置文件 dev.config.js 和 prod.config.js障般,分別對應(yīng)開發(fā)模式和生產(chǎn)模式的兩種不同配置,這樣我們就可以通過 --config 來指定了盛杰。
{
  "script": {
    "webpack:dev": "webpack --config dev.config.js --mode development",
    "webpack:build": "webpack --config prod.config.js"
  }
}

*附上一個(gè) Webpack 配置文件選項(xiàng)詳解挽荡。
*附上一個(gè)前端構(gòu)建配置生成器 Create App

常用 Webpack CLI 接口參數(shù)

需要注意的是即供,命令行接口參數(shù)的優(yōu)先級是高于配置文件參數(shù)的定拟。

參數(shù) 說明 默認(rèn)值
--config 配置文件的路徑 webpack.config.js 或者 webpackfile.js
--mode 用到的模式,development 或 production
--hot 開啟模塊熱替換
--progress 打印出編譯進(jìn)度的百分比值 false
--debug 將 loader 設(shè)置為 debug 模式 false
--color, --colors 強(qiáng)制在控制臺開啟顏色
--watch, -w 觀察文件系統(tǒng)的變化
--env 配置文件導(dǎo)出一個(gè)函數(shù)時(shí)逗嫡,會將此環(huán)境變量傳給該函數(shù)

二办素、Webpack API

1. 入口(entry)

項(xiàng)目的入口文件,從這個(gè)入口文件開始祸穷,應(yīng)用程序啟動執(zhí)行性穿。如果傳遞一個(gè)數(shù)組,那么數(shù)組的每一項(xiàng)都會執(zhí)行雷滚。

不配置入口文件的情況下需曾,Webpack 會默認(rèn)取 src/index.js 作為啟動文件。若不存在祈远,則打包失敗并報(bào)錯(cuò):

ERROR in Entry module not found: Error: Can't resolve './src' in 'xxx'

// 僅舉例說明呆万,實(shí)際情況取其一,下同
module.exports = {
  // 支持 string | array | object | function 形式车份,常用的是數(shù)組和對象的形式
  // 字符串形式谋减,chunk 被命名為 'main'
  entry: 'string',

  // 字符串?dāng)?shù)組形式,chunk 被命名為 'main'
  entry: ['string'],

  // 對象形式扫沼,每個(gè) key 作為 chunk 名稱
  entry: {
    main: 'string or array',
    vendor: 'string or array'
  },

  // 動態(tài)入口(dynamic entry)可使用函數(shù)形式出爹,比如從服務(wù)器獲取等庄吼,我暫時(shí)未用過
  entry: () => {
    // 還可以返回 Promise。
    return 'string | [string]'
  }
}
2. 輸出(output)

它包括了一組選項(xiàng)严就,指示 Webpack 如何去輸出总寻,以及在哪里輸出你的 bundle、asset 和其他你所打包或者使用 Webpack 載入的任何內(nèi)容梢为。

注意整個(gè)配置中我們使用 Node 內(nèi)置的 path 模塊渐行,并在它前面加上 __dirname 這個(gè)全局變量≈可以防止不同操作系統(tǒng)之間的文件路徑問題祟印,并且可以使相對路徑按照預(yù)期工作。我們在很多地方將會使用到它粟害。

__dirname 指當(dāng)前文件所在的目錄
__filename 表示正在執(zhí)行腳本的文件名

需要注意的是旁理,它是兩個(gè)下劃線,兩者均返回一個(gè)絕對路徑我磁。

它的選項(xiàng)很多孽文,主要介紹常用的幾個(gè):

  • path

所有輸出文件的目標(biāo)路徑。它是一個(gè)絕對路徑夺艰,默認(rèn)是項(xiàng)目根目錄下的 dist 路徑芋哭。

例如,打包后的 JS 文件郁副、url-loader 解析的圖片减牺,html-webpack-plugin 生成的 HTML 文件等都會存放到該路徑下(或相對于該路徑的子目錄)

若非絕對路徑,它將會構(gòu)建失敗并報(bào)錯(cuò):configuration.output.path: The provided value "xxx" is not an absolute path!

  • publicPath

publicPath 并不會對生成文件的路徑造成影響存谎,主要是對你的頁面里面引入的資源的路徑做對應(yīng)的補(bǔ)全拔疚,常見的就是 CSS 文件里面引入的圖片。

其中某些 loader(例如 file-loader) 的 publicPath 選項(xiàng)會覆蓋掉 output.publicPath 的既荚。

關(guān)于 pathpublicPath 很多人容易混淆稚失,官方的描述我看起來是模糊的,所以下面我通俗地描述一下恰聘。

通俗地講句各,path 就是打包文件存放在硬盤上的路徑,它不會因?yàn)?publicPath 的設(shè)置而改變晴叨。

publicPath 會影響項(xiàng)目中引用的資源路徑并重寫凿宾。它只會修改項(xiàng)目中的相對路徑和絕對路徑,而完整的絕對路徑將不受影響(例如 https://cdn.example.com/assets/ 這種形式不會被修改)兼蕊。最常見的就是圖片資源初厚、打包產(chǎn)出的 JavaScript 文件在 HTML 中的引用路徑等。這些文件的路徑目錄將被 publicPath 替換重寫(除了文件名不變孙技,其他被替換)产禾。常被用來指定上線后的 cdn 域名排作。

  • filename

此選項(xiàng)決定了每個(gè)(入口 chunk 文件)輸出 bundle 的名稱。這些 bundle 將寫入到 output.path 選項(xiàng)指定的目錄下下愈。

注意纽绍,此選項(xiàng)不會影響那些「按需加載 chunk」的輸出文件蕾久。對于這些文件势似,請使用 output.chunkFilename 選項(xiàng)來控制輸出。通過 loader 創(chuàng)建的文件也不受影響僧著。在這種情況下履因,你必須嘗試 loader 特定的可用選項(xiàng)。

可以使用以下替換模板字符串:

模板 描述
[hash] 模塊標(biāo)識符(module identifier)的 hash
[chunkhash] chunk 內(nèi)容的 hash
[name] 模塊名稱(即入口文件名稱)盹愚,默認(rèn)為 main
[id] 模塊標(biāo)識符(module identifier)
[query] 模塊的 query栅迄,例如文件名 ? 后面的字符串
[function] The function, which can return filename [string]

*[hash][chunkhash] 的長度可以使用 [hash:16](默認(rèn)為 20)來指定。
*如果將這個(gè)選項(xiàng)設(shè)為一個(gè)函數(shù)皆怕,函數(shù)將返回一個(gè)包含上面表格中替換信息的對象毅舆。
*注意此選項(xiàng)被稱為文件名,但是你還是可以使用像 js/[name]/bundle.js 這樣的文件夾結(jié)構(gòu)愈腾。

關(guān)于 Webpack 的 hash憋活、chunkhashcontenthash 的區(qū)別虱黄,可以看下這篇文章悦即。

  • chunkFilename

此選項(xiàng)決定了非入口(non-entry)chunk 文件的名稱。

注意橱乱,這些文件名需要在 runtime 根據(jù) chunk 發(fā)送的請求去生成辜梳。因此,需要在 webpack runtime 輸出 bundle 值時(shí)泳叠,將 chunk id 的值對應(yīng)映射到占位符(如 [name][chunkhash])作瞄。這會增加文件大小,并且在任何 chunk 的占位符值修改后危纫,都會使 bundle 失效粉洼。

默認(rèn)使用 [id].js 或從 output.filename 中推斷出的值([name] 會被預(yù)先替換為 [id][id].),所以它的可讀性很差叶摄。

默認(rèn) [id][name] 是一樣的属韧。

chunkFileName 不能靈活自定義,這誰能忍蛤吓,于是便有了webpackChunkName宵喂,可以看下這篇文章

const path = require('path')

module.exports = {
  output: {
    // 指定打包輸出路徑為 dist会傲,
    // 它必須絕對路徑锅棕,為了避免不同操作系統(tǒng)之間文件路徑問題拙泽,這里借助 Node.js 內(nèi)置的 path 模塊以及 __dirname 全局變量
    // __dirname 是兩個(gè)下劃線
    path: path.resolve(__dirname, 'dist'),



    // 它通常是以 '/' 結(jié)束,避免出現(xiàn)訪問不到生成之后的靜態(tài)資源的問題
    // 實(shí)際場景裸燎,根據(jù)項(xiàng)目本身設(shè)置
    publicPath: '',
    // publicPath: 'https://cdn.example.com/assets/', // CDN(總是 HTTPS 協(xié)議)
    // publicPath: '//cdn.example.com/assets/', // CDN(協(xié)議相同)
    // publicPath: '/assets/', // 相對于服務(wù)(server-relative)
    // publicPath: 'assets/', // 相對于 HTML 頁面
    // publicPath: '../assets/', // 相對于 HTML 頁面
    // publicPath: '', // 相對于 HTML 頁面(目錄相同)顾瞻,默認(rèn)



    // 入口文件輸出 bundle 的名稱
    filename: 'bundle.js', // 靜態(tài)名稱
    // filename: '[name].bundle.js', // 使用入口名稱
    // filename: 'js/[name].bundle.js', // 支持文件夾結(jié)構(gòu)
    // filename: '[id].bundle.js', // 使用內(nèi)部 chunk id
    // filename: '[name].[hash].bundle.js', // 使用每次構(gòu)建過程中,唯一的 hash 生成
    // filename: '[chunkhash].bundle.js', // 使用基于每個(gè) chunk 內(nèi)容的 hash
    // filename: '[contenthash].bundle.css', // Using hashes generated for extracted content
    // filename: (chunkData) => { // Using function to return the filename
    //   // 如果將這個(gè)選項(xiàng)設(shè)為一個(gè)函數(shù)德绿,函數(shù)將返回一個(gè)包含上面表格中替換信息的對象荷荤。
    //   return chunkData.chunk.name === 'main' ? '[name].js' : '[name]/[name].js'
    // },



    // 非入口文件,但參與構(gòu)建的 bundle
    chunkFilename: '[chunkhash].bundle.js' // 可取的值與 filename 一致
  }
}

一句話總結(jié):
filename 指列在 entry 中移稳,打包后輸出的文件的名稱蕴纳。
chunkFilename 指未列在 entry 中,卻又需要被打包出來的文件的名稱个粱。

3. 模塊(module)

這些選項(xiàng)決定了如何處理項(xiàng)目中的不同類型的模塊古毛。

  • noParse

它的作用是防止 webpack 解析那些任意與給定正則表達(dá)式項(xiàng)匹配的文件。因?yàn)樗鼈儽缓雎粤硕夹恚圆粫?Babel 等做語法轉(zhuǎn)換以兼容低版本的瀏覽器稻薇,故它們不應(yīng)該含有 import、require胶征、define 的調(diào)用塞椎。

module.exports = {
  module: {
    // 支持 RegExp、[RegExp]弧烤、function(resource)忱屑、string、[string] 的形式
    noParse: /jquery|loadsh/
    // noParse: content => /jquery|lodash/.test(content)
  }
}
  • rules(重要)

創(chuàng)建模塊時(shí)暇昂,匹配請求的規(guī)則數(shù)組莺戒。這些規(guī)則能夠修改模塊的創(chuàng)建方式报嵌。這些規(guī)則能夠?qū)δK(module)應(yīng)用 loader判沟,或者修改解析器(parser)摘仅。

module.rules 是數(shù)組形式荣倾,支持一個(gè)或多個(gè)規(guī)則,而每個(gè)規(guī)則(Rule)可以分為三部分:條件(condition)蝌以、結(jié)果(result)躏惋、嵌套規(guī)則(nested rule)发框。

  • Rule 條件

    條件有兩種輸入值:
     1. resource:請求文件的絕對路徑泣懊。(它已經(jīng)根據(jù) resolve 規(guī)則解析)
     2. issuer:被請求資源的模塊文件的絕對路徑伸辟,它是導(dǎo)入時(shí)的路徑。

    如果看起來有點(diǎn)懵馍刮,沒關(guān)系信夫,下面舉例說明。

    在規(guī)則中,resource 由屬規(guī)則屬性 test静稻、include警没、excluderesource 對其進(jìn)行匹配振湾。而 issuer 則由規(guī)則屬性 issuer 對其進(jìn)行匹配杀迹。

// 假如我們在入口文件 index.js 導(dǎo)入 app.css
import './styles/app.css?inline'

// webpack 匹配
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        exclude: /node_modules/,
        use: info => {
          // info 是正在加載模塊的一些參數(shù)
          // 包括 resource、issuer押搪、realResource树酪、compiler
          console.log(info)
          return ['style-loader', 'css-loader']
        }
      }
    ]
  }
}

// info 打印結(jié)果如下:
{
  resource: '/Users/frankie/Desktop/Web/Temp/temp_webpack/src/styles/app.css',
  realResource: '/Users/frankie/Desktop/Web/Temp/temp_webpack/src/styles/app.css',
  resourceQuery: '?inline',
  issuer: '/Users/frankie/Desktop/Web/Temp/temp_webpack/src/index.js',
  compiler: undefined
}

結(jié)合概念和例子,其實(shí)已經(jīng)很清楚了嵌言。app.css 是我們的目標(biāo)文件嗅回,而 index.js 則是導(dǎo)入目標(biāo)文件的位置及穗。因此摧茴,resource 就是目標(biāo)文件的絕對路徑,而 issuer 則是 index.js 的絕對路徑埂陆。

  • Rule 結(jié)果
    規(guī)則結(jié)果只有在規(guī)則條件匹配時(shí)使用苛白。
    規(guī)則有兩種輸入值:
     1. 應(yīng)用的 loader:應(yīng)用在 resource 上的 loader 數(shù)組。
     2. Parser 選項(xiàng):用于為模塊創(chuàng)建擠下去的選項(xiàng)對象焚虱。

    這些規(guī)則屬性loader购裙、optionsuse 會影響 loader鹃栽。(query躏率、loaders 也會影響,但它們也被廢棄)
    enforce 屬性會影響 loader 種類民鼓。
    parser 屬性會影響 parser 選項(xiàng)薇芝。

  • 嵌套的 Rule

    可以使用屬性 rulesoneOf 指定嵌套規(guī)則。
    這些規(guī)則用于在規(guī)則條件(rule condition)匹配時(shí)進(jìn)行取值丰嘉。

不知道你們第一次看到上面這些概率描述夯到,會不會有點(diǎn)發(fā)懵,反正我開始看的時(shí)候是會的饮亏。

接下來耍贾,介紹規(guī)則(Rule)的屬性,先看下有哪些:

module.exports = {
  module: {
    rules: [
      // Rule
      {
        resource: {
          test,
          include,
          exclude
        },
        use: [
          {
            loader, 
            options
          }
        ],
        loaders, // 此選項(xiàng)已廢棄路幸,請使用 Rule.use
        query, // 此選項(xiàng)已廢棄荐开,請使用 Rule.use.options
        issuer,
        enforce,
        oneOf, 
        parser,
        resourceQuery,
        rules,
        type,
        sideEffects
      },
      {
        // 可能你們看到更多是長這樣的,但其實(shí)它們只是簡寫罷了简肴。
        // 后面添加配置晃听,我可能使用簡寫多一些。
        test, // Rule.resource.test 的簡寫
        include, // Rule.resource.include 的簡寫
        exclude, // Rule.resource.exclude 的簡寫
        loader, // Rule.use: [ { loader } ] 的簡寫
        options // Rule.use: [ { options } ] 的簡寫
      }
    ]
  }
}
(1) Rule.testRule.include杂伟、Rule.exclude

它們分別是 Rule.resource: { test, inclued, exclued } 的縮寫移层。實(shí)際中,很多開發(fā)的朋友都說采用縮寫的寫法赫粥。

條件可以是這些之一:

  • 字符串:匹配輸入必須以提供的字符串開始观话。是的。目錄絕對路徑或文件絕對路徑越平。
  • 正則表達(dá)式:test 輸入值频蛔。
  • 函數(shù):調(diào)用輸入的函數(shù),必須返回一個(gè)真值(truthy value)以匹配秦叛。
  • 條件數(shù)組:至少一個(gè)匹配條件晦溪。
  • 對象:匹配所有屬性。每個(gè)屬性都有一個(gè)定義行為挣跋。

test:匹配特定條件三圆。一般是提供一個(gè)正則表達(dá)式或正則表達(dá)式的數(shù)組,但這不是強(qiáng)制的避咆。
include:匹配特定條件舟肉。一般是提供一個(gè)字符串或者字符串?dāng)?shù)組,但這不是強(qiáng)制的查库。
exclude:排除特定條件路媚。一般是提供一個(gè)字符串或字符串?dāng)?shù)組,但這不是
強(qiáng)制的樊销。

匹配條件每個(gè)選項(xiàng)都接收一個(gè)正則表達(dá)式或字符串整慎。testinclude 具有相同的作用,都是必須匹配選項(xiàng)围苫。exclude 是必不匹配選項(xiàng)(優(yōu)先于 testinclude

最佳實(shí)踐:

  • 只在 test文件名匹配 中使用正則表達(dá)式裤园。
  • includeexclude 中使用絕對路徑數(shù)組。
  • 盡量避免 exclude够吩,更傾向于使用 include比然。

(2) Rule.use

支持 UseEntriesfunction(info) 兩種方式。

其中 UseEntry 是一個(gè)對象周循,要求必須有一個(gè) loader 屬性是字符串强法。
也可以有一個(gè) options 屬性為字符串或?qū)ο螅渲悼梢詡鬟f到 loader 中湾笛,將其理解為 loader 選項(xiàng)饮怯。
由于兼容性原因,也有可能有 query 屬性嚎研,它是 options 屬性的別名蓖墅。請使用 options 屬性替代库倘。

傳遞字符串(如:use: [ 'style-loader' ])是 loader 屬性的簡寫方式(如:use: [ { loader: 'style-loader' } ]

它還可以傳遞多個(gè) loader,但要注意 loader 的加載順序是從右往左(從下往上)论矾。

Rule.use 也可以是一個(gè)函數(shù)教翩,該函數(shù)接收描述正在加載的模塊的 object 參數(shù),并且必須返回 UseEntry 項(xiàng)的數(shù)組贪壳。
該函數(shù) function(info) 的參數(shù) info 包含以下幾個(gè)字段 { compiler, issuer, realResource, resource }饱亿。
那這幾個(gè)字段究竟是什么呢,其實(shí)上面講述 Rule 條件的時(shí)候闰靴,就有打印出來彪笼,可以往上翻翻,或者看下官網(wǎng)的介紹蚂且。
關(guān)于此我不展開贅述配猫,因?yàn)橐膊恢酪盟鉀Q什么實(shí)際的場景問題,所以其實(shí)沒用過杏死。那說明我目前是不需要它的泵肄,使用 UseEntry 即可滿足我的需求。
我在寫 Redux 篇的時(shí)候识埋,引用過一句話凡伊,用著這里也是同理的零渐。

如果你不知道是否需要 Redux窒舟,那就是不需要它。
module.exports = {
  module: {
    rules: [
      {
        // ...
        // 單個(gè) loader诵盼,可以使用簡寫形式
        loader: 'file-loader',
        options: {
          name: '[name].[ext]'
        }
      },
      {
        // ...
        // 多個(gè) loader惠豺,不含 options 簡寫形式
        use: ['style-loader', 'css-loader'],
      },
      {
        // ...
        // 多個(gè) loader,且含 options 簡寫形式
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1
            }
          },
          {
            loader: 'less-loader',
            options: {
              noIeCompat: true
            }
          }
        ]
      }
    ]
  }
}

(3) Rule.enforce

該屬性指定 loader 種類风宁,其值可以是 pre 或者 post(字符串)洁墙,沒有值表示普通 loader。

所有一個(gè)接一個(gè)地進(jìn)入的 loader戒财,都有兩個(gè)階段:
 1. Pitching 階段:loader 上的 pitch 方法热监,按照 后置(post)行內(nèi)(inline)饮寞、普通(normal)孝扛、前置(pre) 的順序調(diào)用。更多詳細(xì)信息幽崩,請查看 pitching loader苦始。
 2. Normal 階段:loader 上的常規(guī)方法,按照 前置(pre)慌申、普通(normal)陌选、行內(nèi)(inline)后置(post) 的順序調(diào)用。模塊源碼的轉(zhuǎn)換咨油,發(fā)生在這個(gè)階段您炉。

所有普通 loader 可以通過在請求中加上 ! 前綴來忽略(覆蓋)。
所有普通和前置 loader 可以通過在請求中加上 -! 前綴來忽略(覆蓋)役电。
所有普通邻吭,后置和前置 loader 可以通過在請求中加上 !! 前綴來忽略(覆蓋)。

不應(yīng)該使用 行內(nèi) loader! 前綴宴霸,因?yàn)樗鼈兪欠菢?biāo)準(zhǔn)的囱晴。

PS:我沒使用過行內(nèi) loader 的方式,也不太了解它這樣做的目的是什么瓢谢。設(shè)置成前置 loader 倒是用過畸写,前面文章講解 eslint-loaderbabel-loader 順序先后問題用過。

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: path.resolve(__dirname, 'node_modules'),
        loader: 'babel-loader'
      },
      // 由于 eslint-loader 要于 babel-loader 之前執(zhí)行氓扛,且 loader 執(zhí)行順序是從下往上執(zhí)行的枯芬,所以 eslint-loader 要寫在下面
      // 但出于安全謹(jǐn)慎考慮,添加 enforce: 'pre' 屬性采郎,使其無論寫在 babel-loader 前后都能優(yōu)先執(zhí)行千所。
      {
        test: /\.js$/,
        enforce: 'pre',
        exclude: path.resolve(__dirname, 'node_modules'),
        loader: 'eslint-loader',
        options: {
          fix: true
          cache: true
        }
      }
    ]
  }
}

(4) 其他屬性

4. 解析(resolve)

該選項(xiàng)用于配置模塊如何解析竿开。例如谱仪,當(dāng)在 ES6 中調(diào)用 import 'lodash'resolve 選項(xiàng)能夠?qū)?webpack 查找 lodash 的方式去做修改德迹。

這一塊內(nèi)容已在另外一篇文章詳細(xì)介紹了芽卿,請移步至文章 Webpack 如何解析模塊路徑

5. 模式(mode)

提供 mode 配置選項(xiàng)胳搞,告知 webpack 使用響應(yīng)環(huán)境的內(nèi)置優(yōu)化卸例〕蒲睿可選值有:nonedevelopmentproduction筷转。

如果沒有設(shè)置姑原,mode 默認(rèn)設(shè)置為 production∥厥妫可通過以下方式設(shè)定:

// webpack.config.js
module.exports = {
  mode: 'production'
}

或者從 CLI 傳遞參數(shù):

// package.json
{
  "scripts": {
    "build": "webpack --mode production"
  }
}
  • development
    它會將 DefinePlugin 中的 process.env.NODE_ENV 的值設(shè)置為 development锭汛。啟用 NamedChunksPluginNamedModulesPlugin

  • production
    它會將 DefinePlugin 中的 process.env.NODE_ENV 的值設(shè)置為 production袭蝗。啟用 FlagDependencyUsagePlugin唤殴、FlagIncludedChunksPluginModuleConcatenationPlugin到腥、NoEmitOnErrorsPlugin朵逝、OccurrenceOrderPluginSideEffectsFlagPlugin乡范、TerserPlugin配名。

  • none
    它會退出任何默認(rèn)優(yōu)化選項(xiàng)。

注意晋辆,設(shè)置了 NODE_ENV 并不會自動地設(shè)置 mode渠脉。

6. devtool

此選項(xiàng)控制是否生成,以及如何生成 Source Map瓶佳。不同的值會明顯影響到構(gòu)建(build)和重新構(gòu)建(rebuild)的速度芋膘。

建議:開發(fā)環(huán)境使用 eval-cheap-module-source-map,而生產(chǎn)環(huán)境多數(shù)只需要知道報(bào)錯(cuò)的模塊和行號就可以了涩哟,所以使用的是 nosources-source-map索赏。

你可以直接使用 SourceMapDevToolPlugin/EvalSourceMapDevToolPlugin 來替代使用 devtool 選項(xiàng),因?yàn)樗懈嗟倪x項(xiàng)贴彼。切勿同時(shí)使用 devtool 選項(xiàng)和 SourceMapDevToolPlugin/EvalSourceMapDevToolPlugin 插件。devtool 選項(xiàng)在內(nèi)部添加過這些插件埃儿,所以你最終將應(yīng)用兩次插件器仗。

7. 插件(plugins)

該選項(xiàng)用于已各種方式自定義 webpack 構(gòu)建過程。webpack 附帶了各種內(nèi)置的插件剃斧,可以通過 webpack.[plugin-name] 訪問這些插件轨香。

可以查看插件頁面獲取插件列表和對應(yīng)的文檔,這只是其中一部分幼东,社區(qū)中還有很多插件臂容。

每個(gè)插件都是一個(gè)構(gòu)造函數(shù)科雳,使用它的時(shí)候需要用 new 實(shí)例化。

以下是此前系列文章使用過的插件脓杉,后續(xù)文章還將會用到其他插件糟秘,比如 copy-webpack-pluginhappypack 等球散,用到再介紹尿赚。

// webpack.config.js

const webpack = require('webpack')
// 導(dǎo)入非 webpack 自帶默認(rèn)插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  // ...
  plugins: [
    // 創(chuàng)建 HTML 文件
    new HtmlWebpackPlugin({
      title: 'webpack demo',
      template: './src/index.html',
      filename: 'index.html',
      inject: 'body',
      hash: true,
      favicon: './src/favicon.ico'
    }),

    // 新版無需再指定刪除目錄,默認(rèn)刪除 output 的目錄
    new CleanWebpackPlugin(),

    // 通過它啟用 HMR 之后蕉堰,它的接口將被暴露在 module.hot 屬性下面
    new webpack.HotModuleReplacementPlugin(),

    // 允許在編譯時(shí)(compile time)配置的全局常量
    new webpack.DefinePlugin({
      // 注意凌净,因?yàn)檫@個(gè)插件直接執(zhí)行文本替換,給定的值必須包含字符串本身內(nèi)的實(shí)際引號屋讶。通常泻蚊,有兩種方式來達(dá)到這個(gè)效果,使用 '"production"', 或者使用 JSON.stringify('production')丑婿。
      'process.env.NODE_ENV': JSON.stringify('development')
    })
  ]
}
8. 開發(fā)環(huán)境

關(guān)于 watch mode性雄、webpack-dev-serverwebpack-dev-middleware 的選擇羹奉,寫在這篇 Webpack 開發(fā)環(huán)境選擇文章了秒旋。

文章中提到了 webpack-dev-server 生成的包并沒有存儲在你的硬盤中,而是放到了內(nèi)存里诀拭。

接下來介紹的是 webpack-dev-server 選項(xiàng)迁筛。

*若想通過 Node.js API 來使用它,此處有一個(gè)簡單示例耕挨。

webpack-dev-server 支持兩種模式來刷新頁面:

  • iframe:頁面放在 <iframe> 標(biāo)簽中细卧,當(dāng)文件發(fā)生更改會重新刷新頁面,設(shè)置方式有兩種筒占,如下:
module.exports = {
  devServer: {
    inline: false, // 啟用 iframe 模式
    open: true // 在 server 啟動后打開瀏覽器
  }
}

或者通過 CLI 方式:

{
  "scripts": {
    "dev": "webpack-dev-server --inline=false"
  }
}

啟動之后贪庙,打開的 URL 格式如下:

http://?host?:?port?/webpack-dev-server/?path?

# 比如
http://localhost:8080/webpack-dev-server/

*我看過的項(xiàng)目好像還沒有人用這種方式的,我也沒用過翰苫,不展開說了止邮。(PS:我嘗試過這種方式好像只能 Live Reload,不能 HMR奏窑。我不知道是我配置問題导披,還是其他原因?后面有時(shí)間再研究一下埃唯,研究明白了再回來更新這塊內(nèi)容)

  • inline:默認(rèn)是 inline mode撩匕。
    配置方式有三種,看這篇別人踩坑的文章墨叛。我怕我說越多越亂止毕,記住它是默認(rèn)的模式就好了模蜡。

注意接著,往下的內(nèi)容將基于 inline 模式介紹滓技。

告訴服務(wù)器從哪個(gè)目錄中提供內(nèi)容哩牍。只有在你需要提供靜態(tài)文件(如圖片,數(shù)據(jù)等一些不受 webpack 控制的資源文件)時(shí)才需要令漂。devServer.publicPath 將用于確定應(yīng)該從哪里提供 bundle膝昆,并且此選項(xiàng)優(yōu)先。

推薦使用一個(gè)絕對路徑叠必。

默認(rèn)情況下荚孵,將使用當(dāng)前工作目錄作為提供內(nèi)容的目錄,將其設(shè)置為 false 以禁用 contentBase纬朝。

// webpack.config.js
const path = require('path')

module.exports = {
  devServer: {
    // 單個(gè)目錄
    contentBase: path.join(__dirname, 'public'),
    // 多個(gè)目錄
    contentBase: [
      path.join(__dirname, 'public'),
      path.join(__dirname, 'assets')
    ]
  }
}

*CLI 用法不介紹了收叶,下同。

此路徑下的打包文件可在瀏覽器中訪問共苛。devServer.publicPath 默認(rèn)值是 /判没。

假設(shè)服務(wù)器運(yùn)行在 http://localhost:8080 并且 output.filename 被設(shè)置為 bundle.jsdevServer.publicPath 默認(rèn)值是 /隅茎,所以你的包(bundle)可以通過 http://localhost:8080/bundle.js 訪問澄峰。

module.exports = {
  //...
  devServer: {
    publicPath: '/assets/'
  }
}

修改配置,將 bundle 放置指定的目錄下”傧現(xiàn)在通過 http://localhost:8080/assets/bundle.js 訪問到 bundle俏竞。


未完待續(xù)...

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市堂竟,隨后出現(xiàn)的幾起案子魂毁,更是在濱河造成了極大的恐慌,老刑警劉巖出嘹,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件席楚,死亡現(xiàn)場離奇詭異,居然都是意外死亡疚漆,警方通過查閱死者的電腦和手機(jī)酣胀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來娶聘,“玉大人,你說我怎么就攤上這事甚脉⊥枭” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵牺氨,是天一觀的道長狡耻。 經(jīng)常有香客問我墩剖,道長,這世上最難降的妖魔是什么夷狰? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任岭皂,我火速辦了婚禮,結(jié)果婚禮上沼头,老公的妹妹穿的比我還像新娘爷绘。我一直安慰自己,他們只是感情好进倍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布土至。 她就那樣靜靜地躺著,像睡著了一般猾昆。 火紅的嫁衣襯著肌膚如雪陶因。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天垂蜗,我揣著相機(jī)與錄音楷扬,去河邊找鬼。 笑死贴见,一個(gè)胖子當(dāng)著我的面吹牛烘苹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蝇刀,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼螟加,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了吞琐?” 一聲冷哼從身側(cè)響起捆探,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎站粟,沒想到半個(gè)月后黍图,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡奴烙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年助被,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片切诀。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡揩环,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出幅虑,到底是詐尸還是另有隱情丰滑,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布倒庵,位于F島的核電站褒墨,受9級特大地震影響炫刷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜郁妈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一浑玛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧噩咪,春花似錦顾彰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至书在,卻和暖如春灰伟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背儒旬。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工栏账, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人栈源。 一個(gè)月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓挡爵,卻偏偏與公主長得像,于是被迫代替她去往敵國和親甚垦。 傳聞我的和親對象是個(gè)殘疾皇子茶鹃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354