webpack4.0 入門篇 - 構建前端開發的基本環境

guodada發表於2018-09-30

webpack4.0 入門篇 - 構建前端開發的基本環境

什麼是 webpack

webpack 可以看做是模組打包機:他做的事情是,分析你的專案結構,找到 JavaScript 模組以及其他的一些瀏覽器不能直接執行的擴充套件語言(ScssTypeScript 等),將其打包為合適的格式以供瀏覽器使用

構建就是把原始碼轉換成釋出到線上可執行的 JavaScript、CSS、HTML 程式碼,包括以下內容:

  • 程式碼轉換TypeScript 編譯成 JavaScriptSCSS 編譯成 CSS 等等
  • 檔案優化:壓縮 JavaScript、CSS、HTML 程式碼,壓縮合並圖片等
  • 程式碼分割:提取多個頁面的公共程式碼、提取首屏不需要執行部分的程式碼讓其非同步載入
  • 模組合併:在採用模組化的專案有很多模組和檔案,需要構建功能把模組分類合併成一個檔案
  • 自動重新整理:監聽本地原始碼的變化,自動構建,重新整理瀏覽器
  • 程式碼校驗:在程式碼被提交到倉庫前需要檢測程式碼是否符合規範,以及單元測試是否通過
  • 自動釋出:更新完程式碼後,自動構建出線上釋出程式碼並傳輸給釋出系統。

構建其實是工程化、自動化思想在前端開發中的體現。把一系列流程用程式碼去實現,讓程式碼自動化地執行這一系列複雜的流程。

webpack 的基本概念

  • 入口(entry point): 指示 webpack 應該使用哪個模組,來作為構建其內部依賴圖的開始,webpack 會找出有哪些模組和 library 是入口起點(直接和間接)依賴的。

    • 預設值是 ./src/index.js,然而,可以通過在 webpack 配置中配置 entry 屬性,來指定一個不同的入口起點(或者也可以指定多個入口起點)。
  • 出口 output: 屬性告訴 webpack 在哪裡輸出它所建立的 bundles,以及如何命名這些檔案,主輸出檔案預設為 ./dist/main.js,其他生成檔案的預設輸出目錄是 ./dist

  • loader: 讓 webpack 能夠去處理那些非 JavaScript 檔案(webpack 自身只理解 JavaScript)。loader 可以將所有型別的檔案轉換為 webpack 能夠處理的有效模組,然後你就可以利用 webpack 的打包能力,對它們進行處理。

注意,loader 能夠 import 匯入任何型別的模組(例如 .css 檔案),這是 webpack 特有的功能,其他打包程式或任務執行器的可能並不支援。我們認為這種語言擴充套件是有很必要的,因為這可以使開發人員建立出更準確的依賴關係圖。

  • 外掛 plugins: loader 被用於轉換某些型別的模組,而外掛則可以用於執行範圍更廣的任務。外掛的範圍包括,從打包優化和壓縮,一直到重新定義環境中的變數。外掛介面功能極其強大,可以用來處理各種各樣的任務。

  • 模式 mode: 通過選擇 developmentproduction 之中的一個,來設定 mode 引數,你可以啟用相應模式下的 webpack 內建的優化

webpack 構建過程

  1. 從 Entry 裡配置的 Module 開始遞迴解析 Entry 依賴的所有 Module。
  2. 每找到一個 Module, 就會根據配置的 Loader 去找出對應的轉換規則。
  3. 對 Module 進行轉換後,再解析出當前 Module 依賴的 Module。
  4. 這些模組會以 Entry 為單位進行分組,一個 Entry 和其所有依賴的 Module 被分到一個組也就是一個 Chunk。
  5. 最後 Webpack 會把所有 Chunk 轉換成檔案輸出。
  6. 在整個流程中 Webpack 會在恰當的時機執行 Plugin 裡定義的邏輯。

開發環境和生產環境

我們在日常的前端開發工作中,一般都會有兩套構建環境:一套開發時使用,一套供線上使用。

  • development: 用於開發的配置檔案,用於定義 webpack dev server 和其他東西
  • production: 用於生產的配置檔案,用於定義 UglifyJSPluginsourcemaps

簡單來說,開發時可能需要列印 debug 資訊,包含 sourcemap 檔案,而生產環境是用於線上的即程式碼都是壓縮後,執行時不列印 debug 資訊等。譬如 axios、antd 等我們的生產環境中需要使用到那麼我們應該安裝該依賴在生產環境中,而 webpack-dev-server 則是需要安裝在開發環境中

平時我們 npm 中安裝的檔案中有 -S -D, -D 表示我們的依賴是安裝在開發環境的,而-S 的是安裝依賴在生產環境中。

本文就來帶你搭建基本的前端開發環境,前端開發環境需要什麼呢?

  • 構建釋出需要的 HTML、CSS、JS、圖片等資源
  • 使用 CSS 前處理器,這裡使用 less
  • 配置 babel 轉碼器 => 使用 es6+
  • 處理和壓縮圖片
  • 配置熱載入,HMR

以上配置就可以滿足前端開發中需要的基本配置。下面是本文打包後的效果圖:

webpack4.0 入門篇 - 構建前端開發的基本環境

搭建基本的開發環境

安裝

mkdir webpack-dev && cd webpack-dev
npm init -y
npm i webpack webpack-cli -D
複製程式碼

新增 scripts

生成了 package.json 檔案,在檔案中新增

 "scripts": {
    "build": "webpack --mode production"
  }
複製程式碼

--mode 模式 (必選,不然會有 WARNING),是 webpack4 新增的引數選項,預設是 production

  • --mode production 生產環境
    • 提供 uglifyjs-webpack-plugin 程式碼壓縮
    • 不需要定義 new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("production") }) 預設 production
    • 預設開啟 NoEmitOnErrorsPlugin -> optimization.noEmitOnErrors, 編譯出錯時跳過輸出,以確保輸出資源不包含錯誤
    • 預設開啟 ModuleConcatenationPlugin -> optimization.concatenateModules, webpack3 新增的作用域提升(Scope Hoisting)
  • --mode development 開發環境
    • 使用 eval 構建 module, 提升增量構建速度
    • 不需要定義 new webpack.DefinePlugin({ "process.env.NODE_ENV": JSON.stringify("development") }) 預設 development
    • 預設開啟 NamedModulesPlugin -> optimization.namedModules 使用模組熱替換(HMR)時會顯示模組的相對路徑

新增了 scripts 之後,新建src/index.js,然後執行npm run build ,你就會發現新增了一個 dist 目錄,裡邊存放的是 webpack 構建好的 main.js 檔案。

ps npm scripts 使用指南

新建 webpack.config.js 檔案

要想對 webpack 中增加更多的配置資訊,我們需要建立一個 webpack 的配置檔案。在根目錄下建立 webpack.config.js 後再執行 webpack 命令,webpack 就會使用這個配置檔案的配置了

配置中具備以下的基本資訊:

module.exports = {
  entry: '', // 打包入口:指示 webpack 應該使用哪個模組,來作為構建其內部依賴圖的開始
  output: '', // 出口
  resolve: {}, // 配置解析:配置別名、extensions 自動解析確定的擴充套件等等
  devServer: {}, // 開發伺服器:run dev/start 的配置,如埠、proxy等
  module: {}, // 模組配置:配置loader(處理非 JavaScript 檔案,比如 less、sass、jsx、圖片等等)等
  plugins: [] // 外掛的配置:打包優化、資源管理和注入環境變數
}
複製程式碼

配置打包入口和出口

首先我們往 webpack.config.js 新增點配置資訊

const path = require('path')

module.exports = {
  // 指定打包入口
  entry: './src/index.js',

  // 打包出口
  output: {
    path: path.resolve(__dirname, 'dist'), // 解析路徑為 ./dist
    filename: 'bundle.js'
  }
}
複製程式碼

上面我們定義了打包入口 ./src/index.js,打包出口為 ./dist, 打包的資料夾名字為bundle.js,執行npm run build命令後,index.js 檔案會被打包為 bundle.js 檔案。此時隨便建立一個 html 檔案引用這個bundle.js就可以看到你在index.js 寫的程式碼了。

path.resolve([...paths]) 方法會把一個路徑或路徑片段的序列解析為一個絕對路徑。

使用 html-webpack-plugin 建立 html 檔案

更多情況下我們不希望打包一次,就新建一次 html 檔案來引用打包後的檔案,這樣顯得不智慧或者說當你打包的檔名修改後,引用路徑就會出錯。

這個時候我們就可以使用 html-webpack-plugin 外掛來將 HTML 引用路徑和我們的構建結果關聯起來。

npm install html-webpack-plugin -D
複製程式碼

建立檔案public/index.html 修改 webpack.config.js 檔案

const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
  //...
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html', // 配置輸出檔名和路徑
      template: './public/index.html' // 配置要被編譯的html檔案
    })
  ]
}
複製程式碼

重新執行 npm run build, dist 目錄就會多個 index.html 並引入了 bundle.js.

壓縮 html 檔案

修改 webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
  //...
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html', // 配置輸出檔名和路徑
      template: './public/index.html', // 配置要被編譯的html檔案
      hash: true,
      // 壓縮 => production 模式使用
      minify: {
        removeAttributeQuotes: true, //刪除雙引號
        collapseWhitespace: true //摺疊 html 為一行
      }
    })
  ]
}
複製程式碼

打包 css 檔案

我們希望使用 webpack 來進行構建 css 檔案,,為此,需要在配置中引入 loader 來解析和處理 CSS 檔案:

npm install style-loader css-loader -D
複製程式碼

新建 src/assets/style/color.css, 修改 webpack.config.js 檔案:

module.exports = {
  //...
  module: {
    /**
     * test: 匹配特定條件。一般是提供一個正規表示式或正規表示式的陣列
     * include: 匹配特定條件。一般是提供一個字串或者字串陣列
     * exclude: 排除特定條件
     * and: 必須匹配陣列中的所有條件
     * or: 匹配陣列中任何一個條件,
     * nor: 必須排除這個條件
     */
    rules: [
      {
        test: /\.css$/,
        include: [path.resolve(__dirname, 'src')],
        use: ['style-loader', 'css-loader']
      }
    ]
  }
  //...
}
複製程式碼

經由上述兩個 loader 的處理後,CSS 程式碼會轉變為 JS, 如果需要單獨把 CSS 檔案分離出來,我們需要使用 mini-css-extract-plugin 外掛

抽取 css 到獨立檔案, 自動新增字首

npm i mini-css-extract-plugin postcss-loader autoprefixer -D
複製程式碼

我們在寫 css 時不免要考慮到瀏覽器相容問題,如 transform 屬性,需要新增瀏覽器字首以適配其他瀏覽器。故使用到 postcss-loader 這個 loader, 下面則是相關的配置

webpack.config.js

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.css$/,
        include: [path.resolve(__dirname, 'src')],
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              plugins: [require('autoprefixer')]
            }
          }
        ]
      }
    ]
  },
  plugins: [
    //...
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[id].css'
    })
  ]
}
複製程式碼

打包 less 檔案

開發中通常會用到一門預處理語言,這裡以less為例,通過less-loader可以打包 less 為 css 檔案

npm install less less-loader -D
複製程式碼

新建 src/assets/style/index.less, 並且在 src/index.js 中引入 import './assets/style/index.less'

配置 webpack.config.js

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  module: {
    rules: [
      // ...
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              plugins: [require('autoprefixer')] // 新增css中的瀏覽器字首
            }
          },
          'less-loader'
        ]
      }
    ]
  }
  //...
}
複製程式碼

執打包命令後就可以發現 index.less 中寫的樣式會和color.css一樣被打包進 main.css中。

webpack@v4 升級踩坑: 關於使用 mini-css-extract-plugin 的注意點。

打包圖片

npm install file-loader url-loader -D
複製程式碼

file-loader: 可以用於處理很多型別的檔案,它的主要作用是直接輸出檔案,把構建後的檔案路徑返回。

url-loader: 如果圖片較多,會發很多 http 請求,會降低頁面效能。url-loader 會將引入的圖片編碼,生成 dataURl。相當於把圖片資料翻譯成一串字元。再把這串字元打包到檔案中,最終只需要引入這個檔案就能訪問圖片了。當然,如果圖片較大,編碼會消耗效能。因此 url-loader 提供了一個 limit 引數,小於 limit 位元組的檔案會被轉為 DataURl,大於 limit 的還會使用 file-loader 進行 copy。

  • url-loader 可以看作是增強版的 file-loader。
  • url-loader 把圖片編碼成 base64 格式寫進頁面,從而減少伺服器請求。
module.exports = {
  module: {
    rules: [
      // ...
      {
        test: /\.(png|jpg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              outputPath: 'images/', //輸出到images資料夾
              limit: 500 //是把小於500B的檔案打成Base64的格式,寫入JS
            }
          }
        ]
      }
    ]
  }
  //...
}
複製程式碼

url-loader 和 file-loader 是什麼關係呢?

簡單地說,url-loader 封裝了 file-loaderurl-loader 不依賴於 file-loader,即使用 url-loader 時,只需要安裝 url-loader 即可,不需要安裝 file-loader,因為 url-loader 內建了 file-loader

通過上面的介紹,我們可以看到,url-loader 工作分兩種情況:

  • 檔案大小小於 limit 引數,url-loader 將會把檔案轉為 DataURL;
  • 檔案大小大於 limit,url-loader 會呼叫 file-loader 進行處理,引數也會直接傳給 file-loader。因此我們只需要安裝 url-loader 即可。

有關 url-loaderfile-loader 的解析:webpack 之圖片引入-增強的 file-loader:url-loader

配置 babel

babel-loader

Babel 是一個讓我們能夠使用 ES 新特性的 JS 編譯工具,我們可以在 webpack 中配置 Babel,以便使用 ES6、ES7 標準來編寫 JS 程式碼。

Babel 7 的相關依賴包需要加上 @babel scope。一個主要變化是 presets 設定由原來的 env 換成了 @babel/preset-env, 可以配置 targets, useBuiltIns 等選項用於編譯出相容目標環境的程式碼。其中 useBuiltIns 如果設為 "usage",Babel 會根據實際程式碼中使用的 ES6/ES7 程式碼,以及與你指定的 targets,按需引入對應的 polyfill,而無需在程式碼中直接引入 import '@babel/polyfill',避免輸出的包過大,同時又可以放心使用各種新語法特性。

npm i babel-loader @babel/core @babel/preset-env -D
複製程式碼

筆者這裡配的版本號如下

{
  "babel-loader": "^8.0.4",
  "@babel/core": "^7.1.2",
  "@babel/preset-env": "^7.1.0"
}
複製程式碼
  • babel-loader: 用 babel 轉換 ES6 程式碼需要使用到 babel-loader
  • @babel-preset-env: 預設情況下是等於 ES2015 + ES2016 + ES2017,也就是說它對這三個版本的 ES 語法進行轉化。
  • @babel/core:babel 核心庫

根目錄下新建 .babelrc 檔案

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules": false,
        "targets": {
          "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
        },
        "useBuiltIns": "usage"
      }
    ]
  ]
}
複製程式碼
  • presets 是一堆 plugins 的預設,起到方便的作用。
  • plugins 是編碼轉化工具,babel 會根據你配置的外掛對程式碼進行相應的轉化。

修改 webpack.config.js

module.exports = {
  module: {
    rules: [
      //...
      {
        test: /\.m?js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  }
}
複製程式碼

babel/polyfill 和 transform-runtime

Babel 預設只轉換新的 JavaScript 句法(syntax),而不轉換新的 API ,比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全域性物件,以及一些定義在全域性物件上的方法(比如 Object.assign)都不會轉碼。

  • babel-polyfill: 如上述所說,對於新的 API,你可能需要引入 babel-polyfill 來進行相容

  • 關鍵點

    • babel-polyfill 是為了模擬一個完整的 ES2015+環境,旨在用於應用程式而不是庫/工具。
    • babel-polyfill 會汙染全域性作用域

babel-runtime 的作用:

  • 提取輔助函式。ES6 轉碼時,babel 會需要一些輔助函式,例如 _extend。babel 預設會將這些輔助函式內聯到每一個 js 檔案裡, babel 提供了 transform-runtime 來將這些輔助函式“搬”到一個單獨的模組 babel-runtime 中,這樣做能減小專案檔案的大小。
  • 提供 polyfill:不會汙染全域性作用域,但是不支援例項方法如 Array.includes

babel-runtime 更像是分散的 polyfill 模組,需要在各自的模組裡單獨引入,藉助 transform-runtime 外掛來自動化處理這一切,也就是說你不要在檔案開頭 import 相關的 polyfill,你只需使用,transform-runtime 會幫你引入。

對於開發應用來說,直接使用上述的按需 polyfill 方案是比較方便的,但如果是開發工具、庫的話,這種方案未必適合(babel-polyfill 是通過向全域性物件和內建物件的 prototype 上新增方法實現的,會造成全域性變數汙染)。Babel 提供了另外一種方案 transform-runtime,它在編譯過程中只是將需要 polyfill 的程式碼引入了一個指向 core-js 中對應模組的連結(alias)。關於這兩個方案的具體差異和選擇,可以自行搜尋相關教程,這裡不再展開,下面提供一個 transform-runtime 的參考配置方案。

  • 首先安裝 runtime 相關依賴
npm i @babel/plugin-transform-runtime -D
npm i @babel/runtime -S
複製程式碼

修改 .babelrc

{
  //...
  "plugins": ["@babel/plugin-transform-runtime"]
}
複製程式碼

打包前清理源目錄檔案 clean-webpack-plugin

每次打包,都會生成專案的靜態資源,隨著某些檔案的增刪,我們的 dist 目錄下可能產生一些不再使用的靜態資源,webpack 並不會自動判斷哪些是需要的資源,為了不讓這些舊檔案也部署到生產環境上佔用空間,所以在 webpack 打包前最好能清理 dist 目錄。

npm install clean-webpack-plugin -D
複製程式碼

修改 webpack.config.js 檔案

const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = {
  plugins: [new CleanWebpackPlugin(['dist'])]
}
複製程式碼

提取公用程式碼

假如你 a.jsb.js 都 import 了 c.js 檔案,這段程式碼就冗雜了。為什麼要提取公共程式碼,簡單來說,就是減少程式碼冗餘,提高載入速度。

module.exports = {
  //...
  optimization: {
    splitChunks: {
      cacheGroups: {
        commons: {
          // 抽離自己寫的公共程式碼
          chunks: 'initial',
          name: 'common', // 打包後的檔名,任意命名
          minChunks: 2, //最小引用2次
          minSize: 0 // 只要超出0位元組就生成一個新包
        },
        styles: {
          name: 'styles', // 抽離公用樣式
          test: /\.css$/,
          chunks: 'all',
          minChunks: 2,
          enforce: true
        },
        vendor: {
          // 抽離第三方外掛
          test: /node_modules/, // 指定是node_modules下的第三方包
          chunks: 'initial',
          name: 'vendor', // 打包後的檔名,任意命名
          // 設定優先順序,防止和自定義的公共程式碼提取時被覆蓋,不進行打包
          priority: 10
        }
      }
    }
  }
}
複製程式碼

hash

hash 是幹嘛用的? 我們每次打包出來的結果可能都是同一個檔案,那我上線的時候是不是要替換掉上線的 js,那我怎麼知道哪是最新的呢,我們一般會清一下快取。而 hash 就是為了解決這個問題而存在的

我們此時在改一些 webpack.config.js 的配置

module.exports = {
  //...
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[hash:8].js'
  },
  //...
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[hash:8].css',
      chunkFilename: '[id].[hash:8].css'
    })
  ]
}
複製程式碼

減少 resolve 的解析,配置別名

如果我們可以精簡 resolve 配置,讓 webpack 在查詢模組路徑時儘可能快速地定位到需要的模組,不做額外的查詢工作,那麼 webpack 的構建速度也會快一些

module.exports = {
  resolve: {
    /**
     * alias: 別名的配置
     *
     * extensions: 自動解析確定的擴充套件,
     *    比如 import 'xxx/theme.css' 可以在extensions 中新增 '.css', 引入方式則為 import 'xxx/theme'
     *    @default ['.wasm', '.mjs', '.js', '.json']
     *
     * modules 告訴 webpack 解析模組時應該搜尋的目錄
     *   如果你想要新增一個目錄到模組搜尋目錄,此目錄優先於 node_modules/ 搜尋
     *   這樣配置在某種程度上可以簡化模組的查詢,提升構建速度 @default node_modules 優先
     */
    alias: {
      '@': path.resolve(__dirname, 'src'),
      tool$: path.resolve(__dirname, 'src/utils/tool.js') // 給定物件的鍵後的末尾新增 $,以表示精準匹配
    },
    extensions: ['.wasm', '.mjs', '.js', '.json', '.jsx'],
    modules: [path.resolve(__dirname, 'src'), 'node_modules']
  }
}
複製程式碼

webpack-dev-serve

上面講到了都是如何打包檔案,但是開發中我們需要一個本地服務,這時我們可以使用 webpack-dev-server 在本地開啟一個簡單的靜態服務來進行開發。

webpack-dev-server 是 webpack 官方提供的一個工具,可以基於當前的 webpack 構建配置快速啟動一個靜態服務。當 modedevelopment 時,會具備 hot reload 的功能,即當原始碼檔案變化時,會即時更新當前頁面,以便你看到最新的效果。...

npm install webpack-dev-server -D
複製程式碼

package.json 中 scripts 中新增

"start": "webpack-dev-server --mode development"
複製程式碼

預設開啟一個本地服務的視窗 http://localhost:8080/ 便於開發

配置開發伺服器

我們可以對 webpack-dev-server 做針對性的配置

module.exports = {
  // 配置開發伺服器
  devServer: {
    port: 1234,
    open: true, // 自動開啟瀏覽器
    compress: true // 伺服器壓縮
    //... proxy、hot
  }
}
複製程式碼
  • contentBase: 伺服器訪問的根目錄(可用於訪問靜態資源)
  • port: 埠
  • open: 自動開啟瀏覽器

模組熱替換(hot module replacement)

模組熱替換(HMR - Hot Module Replacement)功能會在應用程式執行過程中替換、新增或刪除模組,而無需重新載入整個頁面。主要是通過以下幾種方式,來顯著加快開發速度:

  • 保留在完全重新載入頁面時丟失的應用程式狀態。
  • 只更新變更內容,以節省寶貴的開發時間。
  • 調整樣式更加快速 - 幾乎相當於在瀏覽器偵錯程式中更改樣式。

上面我們 npm start 後修改一次檔案,頁面就會重新整理一次。這樣就存在很大問題了,比如我們使用 redux, vuex 等外掛,頁面一重新整理那麼存放在 redux, vuex 中的東西就會丟失,非常不利於我們的開發。

HMR 配合 webpack-dev-server ,首先我們配置下 webpack.config.js

const webpack = require('webpack')

module.exports = {
  devServer: {
    //...
    hot: true
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin()
    //...
  ]
}
複製程式碼

配置後還不行,因為 webpack 還不知道你要更新哪裡, 修改 src/index.js 檔案, 新增

if (module.hot) {
  module.hot.accept()
}
複製程式碼

重啟服務,npm start 之後,修改引入 index.js 檔案後,頁面就不會重新重新整理了,這便實現了 HMR

但是但是有個問題是,你修改 css/less 等樣式檔案並未發生改變, what ?

HMR 修改樣式表 需要藉助於 style-loader, 而我們之前用的是 MiniCssExtractPlugin.loader, 這也好辦,修改其中一個 rules 就可以了,我們可以試試改

module.exports = {
  module: {
    rules: [
      {
        test: /\.less$/,
        use: [
          // MiniCssExtractPlugin.loader,
          'style-loader',
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              plugins: [require('autoprefixer')] // 新增css中的瀏覽器字首
            }
          },
          'less-loader'
        ]
      }
    ]
  }
}
複製程式碼

這樣我們修改 less 檔案就會發現 HMR 已經實現了。

其實,我們可以發現,dev 下配置的 loader 為 style-loader , 而生產環境下則是需要 MiniCssExtractPlugin.loader

這就涉及到了不同環境之間的配置。可以通過 process.env.NODE_ENV 獲取當前是開發環境或者是生產環境,然後配置不同的 loader,這裡就不做展開了。下一篇文章打算在做一個 react-cli 或者 vue-cli 的配置,將開發環境的配置與生產環境的配置分開為不同的檔案。

結語

前面講到的知識都是 webpack 的一些基礎的知識,更多的資料可以查詢webpack 中文官網,官網講的比較詳細,我這裡也是講最常的配置,也是一篇入門系列的文章,文中涉及的知識點還有很多地方還需要完善,譬如 優化 webpack 的構建速度, 減小打包的體積等等。

學習 webpack 4.0 還需要多實踐,多瞎搞,筆者也是剛剛學習 webpack 的配置,不對之處請各位指出。

下一篇文章打算從零配置一個腳手架,以加深自己對 webpack 的理解。

本文產生的程式碼:webpack-dev

參考

相關文章