【Cute-Webpack】Webpack4 入門(mén)手冊(cè)(共 18 章)

介紹

1. 背景

最近和部門(mén)老大,一起在研究團(tuán)隊(duì)【EFT - 前端新手村】的建設(shè),目的在于:幫助新人快速了解和融入公司團(tuán)隊(duì),幫助零基礎(chǔ)新人學(xué)習(xí)和入門(mén)前端開(kāi)發(fā)并且達(dá)到公司業(yè)務(wù)開(kāi)發(fā)水平。

本文也是屬于【EFT - 前端新手村】的一部分,用來(lái)幫助新人快速入門(mén) Webpack4,內(nèi)容偏基礎(chǔ),當(dāng)然也可以作為復(fù)習(xí)材料~~這里分享給各位小伙伴啦!

2. 文章概要

我將從最基礎(chǔ)的【項(xiàng)目初始化】開(kāi)始介紹,到【處理 CSS / JS / 圖片】,到【熱更新,打包優(yōu)化】等等,一一介紹和實(shí)踐。

文章共分為 18 章,關(guān)于最基礎(chǔ)的四個(gè)核心概念,可以到我整理的另一篇文章 《Webpack4 的四個(gè)核心概念》 中學(xué)習(xí)。

3. 教程目錄

教程目錄

一、 項(xiàng)目初始化

1. 初始化 demo

新建并進(jìn)入文件夾 leo:

mkdir leo
cd leo

然后本地安裝 webpackwebpack-cli在 Webpack 4.0以后需要單獨(dú)安裝):

npm install webpack webpack-cli --save-dev

初始化項(xiàng)目結(jié)構(gòu):

+ ├─package.json
+ ├─dist          // 存放最終打包的文件
+ │  └─index.html
+ ├─src           // 存放入口文件等開(kāi)發(fā)文件
+ │  └─index.js
+ ├─webpack.config.js  // webpack的配置文件

安裝 lodash

npm install lodash --save-dev

--save 可以簡(jiǎn)寫(xiě)為-S, --save-dev可以簡(jiǎn)寫(xiě)為-D.

開(kāi)發(fā) index.js

import _ from 'lodash';

function createElement(){
    let div = document.createElement('div');
    div.innerHTML = _.join(['my', 'name', 'is', 'leo'], '');
    return div;
}
document.body.appendChild(createElement());

開(kāi)發(fā) webpack.config.js

const path = require('path');

module.exports = {
    entry: './src/index.js',
    mode: 'development',
    output: {
        filename: 'main.js',
        path: path.resolve(__dirname, 'dist')
    }
}

2. 打包測(cè)試

開(kāi)始第一次打包任務(wù):

npx webpack

// 輸出:

Hash: 030b37b6b9a0b4344437
Version: webpack 4.39.1Time: 308ms
Built at: 2019-08-07 08:10:21
  Asset     Size  Chunks             Chunk Names
main.js  552 KiB    main  [emitted]  main
Entrypoint main = main.js
[./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 472 bytes {main} [built]
[./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 497 bytes {main} [built][./src/index.js] 225 bytes {main} [built]
    + 1 hidden module

打包成功后,生成的文件會(huì)保存在 dist 目錄中。

現(xiàn)在在 dist/index.html 中引入打包后的 main.js,打開(kāi)瀏覽器測(cè)試:

<script src="./main.js"></script>

二、 webpack 處理 CSS 模塊

這一部分,我們開(kāi)始學(xué)著使用 webpack 去處理 css 相關(guān)的模塊。

1. 修改代碼

在項(xiàng)目 src 目錄中,新建 style 文件夾,并新建 index.css 文件:

  ├─package.json
  ├─dist          // 存放最終打包的文件
  │  └─index.html
  ├─src           // 存放入口文件等開(kāi)發(fā)文件
  │  ├─index.js
+ │  └─style
+ │     └─index.css
  ├─webpack.config.js  // webpack的配置文件

接著在 index.js 的新建元素方法中,添加 classbox,這樣新建的元素就帶有 boxclass 屬性:

// src/index.js

import _ from 'lodash';
import './style/index.css';// 引入樣式文件

function createElement(){
  let div = document.createElement('div');
  div.innerHTML = _.join(['my', 'name', 'is', 'leo'], '');
+ div.className = 'box';
  return div;
}
document.body.appendChild(createElement());

然后在 index.css 文件為 box

// src/style/index.css

.box{
    color: red;
}

注意:

這里使用 import './style/index.css'; 引入我們的樣式文件,是沒(méi)辦法解析使用,這時(shí)我們需要在 webpack 中使用到第三方 loader 插件,這里我們使用:

  • css-loader : 用于處理 css 文件,使得能在 js 文件中引入使用;
  • style-loader : 用于將 css 文件注入到 index.html 中的 <style> 標(biāo)簽上;

2. 安裝配置插件

安裝插件:

npm install --save-dev style-loader css-loader

再到 webpack.config.js 中添加 css 解析的 loader 配置:

// webpack.config.js

module: {
  rules: [
    {
      test: /\.css$/,
      use: ["style-loader", "css-loader"]
    }
  ]
}

參數(shù)介紹:

test需要匹配的模塊后綴名;
use:對(duì)應(yīng)處理的 loader 插件名稱(處理順序是從右往左)。

3. 打包測(cè)試

npx webpack

// 輸出:

Hash: 28b3965aa1b6a0047536
Version: webpack 4.39.1
Time: 482msBuilt at: 2019-08-09 07:45:25  Asset     Size  Chunks             Chunk Names
main.js  565 KiB    main  [emitted]  main
Entrypoint main = main.js
[./node_modules/_css-loader@3.2.0@css-loader/dist/cjs.js!./src/style/index.css] 190 bytes {main} [built]
[./node_modules/webpack/buildin/global.js] (webpack)/buildin/global.js 472 bytes {main} [built][./node_modules/webpack/buildin/module.js] (webpack)/buildin/module.js 497 bytes {main} [built][./src/index.js] 303 bytes {main} [built]
[./src/style/index.css] 447 bytes {main} [built]
    + 3 hidden modules

這時(shí)候可以看見(jiàn) index.html 中,文本已經(jīng)變成紅色,并且 css 代碼已經(jīng)添加到 <style> 標(biāo)簽上。

webpack01

三、 webpack 模塊介紹和處理 sass

在這一節(jié)中,我們會(huì)介紹 webpack 中的模塊,并且介紹如何去處理 sass 文件。

1. webpack 模塊介紹

這里介紹的模塊(module)是指 webpack.config.js 文件中的 module 配置,它決定了如何處理項(xiàng)目中的不同類型模塊。

比如上一節(jié)介紹的,使用 style-loadercss-loader 兩個(gè)插件去處理 css 文件。

webpack 模塊支持如下語(yǔ)句:

  • ES2015 import 語(yǔ)句;
  • CommonJS require() 語(yǔ)句;
  • AMD definerequire 語(yǔ)句;
  • css/sass/less 文件中 @import 語(yǔ)句;
  • 樣式 (url(...)) 或者 HTML 文件 (<img src=...>) 中的圖片鏈接 (image url)

這里建議使用 ES2015 的引入方法,畢竟這是標(biāo)準(zhǔn)。

更多參數(shù)介紹,可訪問(wèn)中文官網(wǎng)的介紹:
《webpack 配置選項(xiàng)》

2. 常用模塊

2.1 module.noParse

值的類型:RegExp | [RegExp] | function

防止 webpack 解析那些符合匹配條件的文件,忽略的文件夾中不應(yīng)該含有 import、require、define的調(diào)用,或任何其他導(dǎo)入機(jī)制,忽略的 library 可以提高構(gòu)建效率。

// webpack.config.js

module: {
  noParse: function(content){
    return /jquery|lodash/.test(content);
  }
}

2.2 module.rules

創(chuàng)建模塊時(shí),匹配請(qǐng)求的規(guī)則數(shù)組。按照規(guī)則為對(duì)應(yīng)模塊使用對(duì)應(yīng)的 loader,或修改解析器(parser)。

// webpack.config.js

module: {
  rules: [
    { test: /\.css$/, use: ['style-loader', 'css-loader']}
  ]
}
  • module.rules 參數(shù)有:

use:為模塊使用指定 loader,并且可以傳入一個(gè)字符串?dāng)?shù)組,加載順序從右往左

  • module.rules 匹配條件有:

{test : Condition}匹配特定條件,非必傳,支持一個(gè)正則表達(dá)式正則表達(dá)式數(shù)組
{include : Condition}匹配特定條件,非必傳,支持一個(gè)字符串字符串?dāng)?shù)組;
{exclude : Condition}排除特定條件,非必傳,支持一個(gè)字符串字符串?dāng)?shù)組;
{and : [Condition]}:必須匹配數(shù)組中的所有條件;
{or : [Condition]}:匹配數(shù)組中任一條件;
{not : [Condition]}:必須排除這個(gè)條件;

更多參數(shù)介紹,可訪問(wèn)中文官網(wǎng)的介紹:
《Rule》

// webpack.config.js

module: {
  rules: [
    { 
      test: /\.css$/, 
      use: ['style-loader', 'css-loader'],
      include: [
        path.resolve(__dirname, "app/style.css"),
        path.resolve(__dirname, "vendor/style.css")
      ]
    }
  ]
}

3. 加載 Sass 文件

需要使用到 sass-loader 的插件,這里先安裝:

npm install sass-loader node-sass --save-dev

src/style 目錄下添加 leo.scss 文件,并添加內(nèi)容:

// leo.scss

$bg-color: #ee3;
.box{
    background-color: $bg-color;
}

然后在 src/index.js 中引入 leo.scss 文件:

// src/index.js
import './style/leo.scss';

npx webpack 重新打包,并打開(kāi) dist/index.html 可以看到背景顏色已經(jīng)添加上去:

webpack03

4. 添加快捷打包命令

npx webpack 這個(gè)命令我們需要經(jīng)常使用,對(duì)于這種命令,我們可以把它寫(xiě)成命令,方便每次使用。

我們?cè)?package.jsonscripts 中添加一個(gè)命令為 build,以后打包只要執(zhí)行 npm run build 即可:

"scripts": {
  "build": "npx webpack -c webpack.config.js"
},

這里的 -c webpack.config.js 中,-c 后面跟著的是 webpack 配置文件的文件名,默認(rèn)可以不寫(xiě)。

四、 webpack 開(kāi)啟 SourceMap 和添加 CSS3 前綴

添加 SourceMap 是為了方便打包之后,我們?cè)陧?xiàng)目中調(diào)試樣式,定位到樣式在源文件的位置。

1. 開(kāi)啟 SourceMap

css-loadersass-loader 都可以通過(guò)設(shè)置 options 選項(xiàng)啟用 sourceMap。

// webpack.config.js

rules: [
  {
    test: /\.(sc|c|sa)ss$/,
    use: [
      "style-loader", 
      {
        loader:"css-loader",
        options:{ sourceMap: true }
      },
      {
        loader:"sass-loader",
        options:{ sourceMap: true }
      },
    ]
  }
]

再重新打包,看下 index.html 的樣式,樣式已經(jīng)定位到源文件上了:

webpack04

這樣我們?cè)陂_(kāi)發(fā)過(guò)程中,調(diào)試樣式就方便很多了。

2. 為樣式添加 CSS3 前綴

這里我們用到 PostCSS 這個(gè) loader,它是一個(gè) CSS 預(yù)處理工具,可以為 CSS3 的屬性添加前綴,樣式格式校驗(yàn)(stylelint),提前使用 CSS 新特性,實(shí)現(xiàn) CSS 模塊化,防止 CSS 樣式?jīng)_突。

首先安裝 PostCSS

npm install postcss-loader autoprefixer --save-dev

另外還有:

  • postcss-cssnext 可以讓我們使用 CSS4的樣式,并能配合 autoprefixer 進(jìn)行瀏覽器部分兼容的補(bǔ)全,還支持嵌套語(yǔ)法。

  • precss 類似 scss 語(yǔ)法,如果我們只需要使用嵌套,就可以用它替換 scss

  • postcss-import 讓我們可以在@import CSS文件的時(shí) webpack 能監(jiān)聽(tīng)并編譯。

更多參數(shù)介紹,可訪問(wèn)中文官網(wǎng)的介紹:
《postcss-loader》

開(kāi)始添加 postcss-loader 并設(shè)置 autoprefixer

// webpack.config.js

rules: [
  {
    test: /\.(sc|c|sa)ss$/,
    use: [
      "style-loader", 
      {
        loader:"css-loader",
        options:{ sourceMap: true }
      },
      {
        loader:"postcss-loader",
        options: {
          ident: "postcss",
          sourceMap: true,
          plugins: loader => [
            require('autoprefixer')(),
            // 這里可以使用更多配置,如上面提到的 postcss-cssnext 等
            // require('postcss-cssnext')()
          ]
        }
      },
      {
        loader:"sass-loader",
        options:{ sourceMap: true }
      },
    ]
  }
]

還需要在 package.json 中添加判斷瀏覽器版本:

// package.json

{
  //...
  "browserslist": [
    "> 1%", // 全球?yàn)g覽器使用率大于1%,最新兩個(gè)版本并且是IE8以上的瀏覽器,加前綴 
    "last 2 versions",
    "not ie <= 8"
  ]
}

為了做測(cè)試,我們修改 src/style/leo.scss.box 的樣式:

// src/style/leo.scss

.box{
    background-color: $bg-color;
    display: flex;
}

然后重新打包,可以看見(jiàn) CSS3 屬性的前綴已經(jīng)添加上去了:

webpack05

五、 webpack 將 CSS 抽取成單獨(dú)文件

在之前學(xué)習(xí)中,CSS 樣式代碼都是寫(xiě)到 index.html<style> 標(biāo)簽中,這樣樣式代碼多了以后,很不方便。

于是我們需要將這些樣式打包成單獨(dú)的 CSS 文件。

webpack4 開(kāi)始使用 mini-css-extract-plugin 插件,而在 1-3 版本使用 extract-text-webpack-plugin

注意:抽取樣式以后,就不能使用 style-loader 注入到 html 中。

安裝插件:

npm install mini-css-extract-plugin --save-dev

引入插件:

// webpack.config.js

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

然后修改 rules,將 style-loader,替換成 MiniCssExtractPlugin.loader ,然后添加 plugins 配置項(xiàng):

// webpack.config.js

module: {
  rules: [
    {
      test: /\.(sc|c|sa)ss$/,
      use: [
        MiniCssExtractPlugin.loader, 
        {
          loader:"css-loader",
          options:{ sourceMap: true }
        },
        {
          loader:"postcss-loader",
          options: {
            ident: "postcss",
            sourceMap: true,
            plugins: loader => [require('autoprefixer')()]
          }
        },
        {
          loader:"sass-loader",
          options:{ sourceMap: true }
        },
      ]
    }
  ]
},
plugins: [
  new MiniCssExtractPlugin({
    filename: '[name].css', // 最終輸出的文件名
    chunkFilename: '[id].css'
  })
]

然后重新打包,這時(shí)候可以看到我們 dist 目錄下就多了個(gè) main.css 文件:

webpack06

因?yàn)楝F(xiàn)在已經(jīng)將 CSS 都抽取成單獨(dú)文件,所以在 dist/index.html 中,我們需要手動(dòng)引入 main.css 了:

// index.html

<link rel="stylesheet" href="main.css">

六、 webpack 壓縮 CSS 和 JS

為了縮小打包后包的體積,我們經(jīng)常做優(yōu)化的時(shí)候,將 CSS 和 JS 文件進(jìn)行壓縮,這里需要使用到不同的插件。

1. 壓縮 CSS

使用 optimize-css-assets-webpack-plugin 壓縮 CSS 的插件。

安裝插件:

npm install optimize-css-assets-webpack-plugin --save-dev

使用插件:

// webpack.config.js

// ... 省略
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
  // ... 省略
  plugins: [
    // ... 省略
    new OptimizeCssAssetsPlugin({})
  ],
}

重新打包,可以看到 main.css 已經(jīng)被壓縮成一行代碼,即壓縮成功~

2. 壓縮 JS

使用 uglifyjs-webpack-plugin 壓縮 JS 的插件。

安裝插件:

npm install uglifyjs-webpack-plugin --save-dev

引入插件:

// webpack.config.js

const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

使用插件:

// webpack.config.js
// ... 省略
module.exports = {
  // ... 省略
  plugins: [
    // ... 省略
    new OptimizeCssAssetsPlugin({}),
    new UglifyJsPlugin({
      cache: true, parallel: true, sourceMap: true
    })
  ],
}

其中 UglifyJsPlugin 的參數(shù):

cache:當(dāng) JS 沒(méi)有發(fā)生變化則不壓縮;
parallel:是否啟用并行壓縮;
sourceMap:是否啟用 sourceMap;

然后重新打包,查看 main.js,已經(jīng)被壓縮了:

webpack07

七、webpack 為文件名添加 hash 值

由于我們打包出來(lái)的 cssjs 文件是靜態(tài)文件,就存在緩存問(wèn)題,因此我們可以給文件名添加 hash 值,防止緩存。

1. 添加 hash 值

直接在 webpack.config.js 中,為需要添加 hash 值的文件名添加 [hash] 就可以:

// webpack.config.js

module.exports = {
  // ... 省略其他
  output: {
    filename: 'main.[hash].js',
    path: path.resolve(__dirname, 'dist')
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[hash].css',
      chunkFilename: '[id].[hash].css'
    }),
  ],
}

配置完成后,重新打包,就可以看到文件名中包含了 hash 值了:

webpack08

2. 動(dòng)態(tài)引用打包后的文件

由于我們前面給打包的文件名添加了 hash 值,會(huì)導(dǎo)致 index.html 引用文件錯(cuò)誤,所以我們需要讓它能動(dòng)態(tài)引入打包后的文件。

這里我們使用 HtmlWebpackPlugin 插件,它可以把打包后的 CSS 或者 JS 文件直接引用注入到 HTML 模版中,就不用每次手動(dòng)修改。

安裝插件:

npm install html-webpack-plugin --save-dev

引入插件:

// webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');

使用插件:

// webpack.config.js

plugins: [
  new HtmlWebpackPlugin({
    title: "leo study!",   // 生成的文件標(biāo)題
    filename: "main.html", // 最終生成的文件名
    minify: { // 壓縮選項(xiàng)
      collapseWhitespace: true, // 移除空格
      removeComments: true, // 移除注釋
      removeAttributeQuotes: true, // 移除雙引號(hào)
    }
  })
],

關(guān)于 html-webpack-plugin 更多介紹可以《查看文檔》https://github.com/jantimon/html-webpack-plugin/。

接著我們打包以后,可以看見(jiàn) dist 目錄下,多了 main.html 的文件,格式化以后,可以看出,已經(jīng)動(dòng)態(tài)引入打包后的 CSS 文件和 JS 文件了:

webpack09

八、 webpack 清理目錄插件

在之前,我們每次打包都會(huì)生成新的文件,并且在添加 hash 值以后,文件名不會(huì)出現(xiàn)重復(fù)的情況,導(dǎo)致舊文件的冗余。

為了解決這個(gè)問(wèn)題,我們需要在每次打包之前,將 /dist 目錄清空,再進(jìn)行打包。

這里我們使用 clean-webpack-plugin 插件來(lái)實(shí)現(xiàn)。

安裝插件:

npm install clean-webpack-plugin --save-dev

引入插件:

// webpack.config.js

const CleanWebpackPlugin = require('clean-webpack-plugin');

使用插件:

// webpack.config.js

plugins: [
  new CleanWebpackPlugin()
],

參數(shù) cleanOnceBeforeBuildPatterns 是表示需要清除的文件夾。

這樣我們每次打包之前,都會(huì)先將 /dist 目錄清空一次,再執(zhí)行打包。

更多參數(shù)介紹,可訪問(wèn)中文官網(wǎng)的介紹:
《clean-webpack-plugin》

九、 webpack 圖片處理和優(yōu)化

1. 圖片處理

在項(xiàng)目中引入圖片:

// src/style/leo.scss

.box{
    background-color: $bg-color;
    display: flex;
    background: url('./../assets/logo.jpg')
}

這時(shí)候我們?nèi)绻苯哟虬瑫?huì)報(bào)錯(cuò)。

我們需要使用 file-loader 插件來(lái)處理文件導(dǎo)入的問(wèn)題。

安裝插件:

npm install file-loader --save-dev

使用插件:

// webpack.config.js

module: {
  {
    test: /\.(png|svg|jpg|jpeg|gif)$/,
    use: ["file-loader"]
  }]
},

重新打包以后,發(fā)現(xiàn) dist 目錄下多了一個(gè)如 373e5e0e214390f8aa9e7abb4c7c635c.jpg 名稱的文件,這就是我們打包后的圖片。

webpack10

2. 圖片優(yōu)化

更進(jìn)一步,我們可以對(duì)圖片進(jìn)行壓縮和優(yōu)化,這里我們用到 image-webpack-loader 插件來(lái)處理。

安裝插件:

npm install image-webpack-loader --save-dev

使用插件:

// webpack.config.js

module: {
  {
    test: /\.(png|svg|jpg|jpeg|gif)$/,
    include: [path.resolve(__dirname, 'src/')],
    use: ["file-loader",{
        loader: "image-webpack-loader",
        options: {
          mozjpeg: { progressive: true, quality: 65 },
          optipng: { enabled: false },
          pngquant: { quality: '65-90', speed: 4 },
          gifsicle: { interlaced: false },
          webp: { quality: 75 }
        }
      },
    ]
  }]
},

更多參數(shù)介紹,可訪問(wèn)中文官網(wǎng)的介紹:
《image-webpack-loader》

再重新打包,我們可以看到圖片打包前后,壓縮了很大:

webpack11

十、 webpack 圖片 base64 和字體處理

1. 圖片 base64 處理

url-loader 功能類似于 file-loader,可以將 url 地址對(duì)應(yīng)的文件,打包成 base64 的 DataURL,提高訪問(wèn)效率。

安裝插件:

npm install url-loader --save-dev

使用插件:

注意:這里需要將前面配置的 image-webpack-loader 先刪除掉,在使用 url-loader。

// webpack.config.js

module: {
  {
    test: /\.(png|svg|jpg|jpeg|gif)$/,
    include: [path.resolve(__dirname, 'src/')],
    use: [
      {
        loader: 'url-loader', // 根據(jù)圖片大小,把圖片轉(zhuǎn)換成 base64
          options: { limit: 10000 }, 
      },
      {
        loader: "image-webpack-loader",
        options: {
          mozjpeg: { progressive: true, quality: 65 },
          optipng: { enabled: false },
          pngquant: { quality: '65-90', speed: 4 },
          gifsicle: { interlaced: false },
          webp: { quality: 75 }
        }
      },
    ]
  }]
},

更多參數(shù)介紹,可訪問(wèn)中文官網(wǎng)的介紹:
《url-loader》

2. 字體處理

字體處理的方式和圖片處理方式是一樣的,只是我們?cè)谂渲?rules 時(shí)的 test 值不相同:

// webpack.config.js

module: {
  {
    test: /\.(woff|woff2|eot|ttf|otf)$/,
    include: [path.resolve(__dirname, 'src/')],
    use: [ 'file-loader' ]
  }
},

十一、 webpack 配置合并和提取公共配置

在開(kāi)發(fā)環(huán)境(development)和生產(chǎn)環(huán)境(production)配置文件有很多不同,但也有部分相同,為了不每次更換環(huán)境的時(shí)候都修改配置,我們就需要將配置文件做合并,和提取公共配置。

我們使用 webpack-merge 工具,將兩份配置文件合并。

安裝插件:

npm install webpack-merge --save-dev

然后調(diào)整目錄結(jié)構(gòu),為了方便,我們將原來(lái) webpack.config.js 文件修改名稱為 webpack.commen.js,并復(fù)制兩份相同的文件出來(lái),分別修改文件名為 webpack.prod.jswebpack.dev.js

  ├─package.json
  ├─dist
  ├─src
- ├─webpack.config.js
+ ├─webpack.common.js  // webpack 公共配置文件
+ ├─webpack.prod.js    // webpack 生產(chǎn)環(huán)境配置文件
+ ├─webpack.dev.js     // webpack 開(kāi)發(fā)環(huán)境配置文件

由于我們文件調(diào)整了,所以在 package.json 中,打包命令也需要調(diào)整,并且配置 mode 模式。

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
- "build": "npx webpack -c webpack.config.js",
+ "build": "npx webpack -c webpack.dev.js --mode development",
+ "dist": "npx webpack -c webpack.prod.js --mode production"
},

1. 調(diào)整 webpack.common.js

我們先調(diào)整 webpack.common.js 文件,將通用的配置保留,不是通用的配置刪除,結(jié)果如下:

// webpack.common.js

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

module.exports = {
  entry: './src/index.js',
  module: {
    noParse: function (content) {return /jquery|lodash/.test(content);},
    rules: [
    {
      test: /\.(png|svg|jpg|jpeg|gif)$/,
      include: [path.resolve(__dirname, 'src/')],
      use: [{
        loader: 'url-loader', // 根據(jù)圖片大小,把圖片轉(zhuǎn)換成 base64
        options: { limit: 10000 },
      },{
        loader: "image-webpack-loader",
        options: {
          mozjpeg: { progressive: true, quality: 65 },
          optipng: { enabled: false },
          pngquant: { quality: '65-90', speed: 4 },
          gifsicle: { interlaced: false },
          webp: { quality: 75 }
        }
      }]
    },{
      test: /\.(woff|woff2|eot|ttf|otf)$/,
      include: [path.resolve(__dirname, 'src/')],
      use: [ 'file-loader' ]
    }]
  },
  plugins: [
      new HtmlWebpackPlugin({
          title: "leo study!",
          filename: "main.html",
          template: path.resolve(__dirname, 'src/index.html'), 
          minify: {
              collapseWhitespace: true,
              removeComments: true,
              removeAttributeQuotes: true,
          }
      }),
      new CleanWebpackPlugin()
  ],
}

2. 安裝 babel-loader

安裝 babel-loader 是為了將 ES6 及以上版本的 JS 代碼轉(zhuǎn)換成 ES5。

npm install babel-loader @babel/core @babel/preset-env --save-dev

使用插件:

// webpack.common.js

rules: [
  // ... 省略其他
  {
    test: /\.js$/,
    use: [{
      loader: 'babel-loader',
      options: {
        presets: ['@babel/preset-env']
      }
    }],
    exclude: /(node_modules|bower_components)/,
  }
]

關(guān)于 babel-loader 更多介紹可以《查看文檔》https://webpack.js.org/loaders/babel-loader/。

3. 調(diào)整 webpack.dev.js

這里我們就需要用到 merge-webpack 插件進(jìn)行配置合并了:

// webpack.dev.js

const path = require('path');
const merge = require('webpack-merge');
const common = require('./webpack.common.js');

let devConfig = {
  mode: 'development',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [{
      test: /\.(sc|c|sa)ss$/,
      use: [
        'style-loader', {
          loader: "css-loader",
          options: { sourceMap: true }
        }, {
          loader: "postcss-loader",
          options: {
              ident: "postcss", sourceMap: true,
              plugins: loader => [ require('autoprefixer')() ]
          }
        }, {
          loader: "sass-loader",
          options: { sourceMap: true }
        }
      ]
    }]
  }
}
module.exports = merge(common, devConfig)

4. 調(diào)整 webpack.prod.js

同樣對(duì)于生產(chǎn)環(huán)境的配置,我們也需要用 merge-webpack 插件進(jìn)行配置合并:

// webpack.prod.js

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const merge = require('webpack-merge');
const common = require('./webpack.common.js');

let prodConfig = {
  mode: 'production',
  output: {
    filename: 'main.[hash].js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [{
      test: /\.(sc|c|sa)ss$/,
      use: [
        MiniCssExtractPlugin.loader, {
          loader: "css-loader",
          options: { sourceMap: true }
        },  {
          loader: "postcss-loader",
          options: {
            ident: "postcss", sourceMap: true,
            plugins: loader => [ require('autoprefixer')() ]
          }
        }, {
          loader: "sass-loader",
          options: { sourceMap: true }
        }
      ]
    }]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[hash].css',
      chunkFilename: '[id].[hash].css'
    }),
    new OptimizeCssAssetsPlugin({}),
    new UglifyJsPlugin({
      cache: true, parallel: true, sourceMap: true
    }),
  ],
}
module.exports = merge(common, prodConfig)

十二、 webpack 監(jiān)控自動(dòng)編譯和啟用 js 的 sourceMap

1. 開(kāi)啟 js 的 sourceMap

當(dāng) webpack 打包源代碼后,就很難追蹤到錯(cuò)誤和警告在源代碼的位置。

如將三個(gè)源文件打包一個(gè) bundle 中,其中一個(gè)文件的代碼報(bào)錯(cuò),那么堆棧追中就會(huì)指向 bundle。

為了能方便定位錯(cuò)誤,我們使用 inline-source-map 選項(xiàng),注意不要在生產(chǎn)環(huán)境中使用。

// webpack.dev.js

let devConfig = {
  // ... 省略其他
+  devtool: 'inline-source-map'
}

2. 測(cè)試 sourceMap

為了測(cè)試是否成功,我們將 src/index.js 代碼中,在第 12 行上,添加一句日志打印。

// src/index.js

// ... 省略其他
+ console.log(111)

對(duì)比下開(kāi)啟 sourceMap 前后的區(qū)別:

webpack12

3. 開(kāi)啟監(jiān)控自動(dòng)編譯

如果每次我們修改完代碼,都要手動(dòng)編譯,那是多累的一件事。

為此我們使用 --watch 命令,讓我們每次保存完,都會(huì)自動(dòng)編譯。

為此,我們需要在 package.json 中的打包命令添加 --watch 命令:

// package.json

- "build": "npx webpack --config webpack.dev.js",
+ "build": "npx webpack --config webpack.dev.js --watch",

這里僅對(duì)開(kāi)發(fā)環(huán)境開(kāi)啟,生產(chǎn)環(huán)境不需要使用。

十三、 webpack 熱更新

上一節(jié)介紹監(jiān)控自動(dòng)編譯,當(dāng)我們保存文件后,會(huì)自動(dòng)編譯文件,但是我們還是需要手動(dòng)去刷新頁(yè)面,才能看到編譯后的結(jié)果。

于是為了自動(dòng)編譯之后,再自動(dòng)重新加載,我們就可以使用 webpack-dev-server 來(lái)啟動(dòng)一個(gè)簡(jiǎn)單 web 服務(wù)器,實(shí)時(shí)重新加載。

1. 開(kāi)啟熱更新

插件安裝:

npm install webpack-dev-server --save-dev

使用插件:

// webpack.dev.js

const webpack = require('webpack');
const webpack = require('webpack');

let devConfig = {
  // ... 省略其他
  devServer: {
    contentBase: path.join(__dirname, 'dist'), 
    compress: true,
    hot: true,
    overlay: true, 
    open:true,
    publicPath: '/',
    host: 'localhost',
    port: '1200'
 }
 plugins: [
    new webpack.NamedModulesPlugin(), // 更容易查看(patch)的以來(lái)
    new webpack.HotModuleReplacementPlugin() // 替換插件
 ]
}

啟動(dòng)熱更新:

npx webpack-dev-server --config webpack.dev.js

常用配置:

contentBase: path.join(__dirname, 'dist'), //本地服務(wù)器所加載的頁(yè)面所在的目錄
clinetLogLevel: 'warning', // 可能值有 none, error, warning 或者 info (默認(rèn)值)
hot:true,//啟動(dòng)熱更新替換特性,需要配合 webpack.HotModuleReplacementPlugin 插件
host:'0.0.0.0', // 啟動(dòng)服務(wù)器的 host
port:7000,      // 端口號(hào)
compress:true,  // 為所有服務(wù)啟用gzip壓縮
overlay: true,  // 在瀏覽器中顯示全屏覆蓋
stats: "errors-only" ,// 只顯示包中的錯(cuò)誤
open:true, // 啟用“打開(kāi)”后,dev服務(wù)器將打開(kāi)瀏覽器。
proxy: {   // 設(shè)置代理
    "/api": {
        target: "http://localhost:3000",
        pathRewrite: {"^/api" : ""}
    }
}

這時(shí)候我們?cè)L問(wèn) http://localhost:1200/main.html 就可以看到頁(yè)面,并且修改文件,頁(yè)面也會(huì)同時(shí)刷新。

2. 優(yōu)化命令

我們可以將 npx webpack-dev-server --config webpack.dev.js 寫(xiě)到 package.json 中作為一個(gè)命令:

// package.json

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "build": "npx webpack --config webpack.dev.js --watch",
  "dist": "npx webpack --config webpack.prod.js",
+ "watch": "npx webpack-dev-server --config webpack.dev.js"
},

十四、 webpack 設(shè)置代理服務(wù)器和 babel 轉(zhuǎn)換及優(yōu)化

1. 設(shè)置代理服務(wù)器

接著上一節(jié),接下來(lái)給 webpack 設(shè)置代理服務(wù)器:

// webpack.dev.js

let devConfig = {
  // ... 省略其他
  devServer: {
    // ... 省略其他
    proxy: { 
      "/api": { // 以 '/api' 開(kāi)頭的請(qǐng)求,會(huì)跳轉(zhuǎn)到下面的 target 配置
        target: "http://192.168.30.33:8080",
        pathRewrite: {
          "^api": "/mock/api"
        }
    }
 }
}

最后當(dāng)我們請(qǐng)求 /api/getuser 接口,就會(huì)轉(zhuǎn)發(fā)到 http://192.168.30.33:8080/mock/api。

2. babel 轉(zhuǎn)換及優(yōu)化

babel-loader 插件的安裝,已經(jīng)提前介紹,在【十一、 webpack 配置合并和提取公共配置】中。

這里講一下 babel-loader 的優(yōu)化。

babel-loader 可以配置 cacheDirectory 來(lái)提高打包效率:

  • cacheDirectory:默認(rèn)值 false,開(kāi)啟后構(gòu)建時(shí)會(huì)緩存文件夾,后續(xù)從緩存中讀取,將提高打包效率。

十五、 webpack 開(kāi)啟 Eslint

安裝插件:

npm install eslint eslint-loader --save-dev

另外還需要安裝 eslint 解釋器、校驗(yàn)規(guī)則等:

npm install babel-loader standard --save-dev

2. 添加 .eslintrc.js

在項(xiàng)目根目錄創(chuàng)建 .eslintrc.js,指定 eslint 規(guī)則。

這份配置內(nèi)容有點(diǎn)多,可以去 我的 gist 復(fù)制https://gist.github.com/pingan8787/8b9abe4e04bed85f9d7846e513ed2e11 。

3. 添加 .eslintignore

在項(xiàng)目根目錄創(chuàng)建 .eslintignore,指定 eslint 忽略一些文件不校驗(yàn),比如內(nèi)容可以是:

/dist/
/node_modules/

十六、 webpack 解析模塊拓展名和別名

在 webpack 配置中,我們使用 resolve 來(lái)配置模塊解析方式。

這是非常重要的,比如 import _ from 'lodash' ,其實(shí)是加載解析了 lodash.js 文件。

該配置就是用來(lái)設(shè)置加載和解析的方式。

在解析過(guò)程中,我們可以進(jìn)行配置:

1. resolve.alias

當(dāng)我們引入一些文件時(shí),需要寫(xiě)很長(zhǎng)的路徑,這樣使得代碼更加復(fù)雜。

為此我們可以使用 resolve.alias,創(chuàng)建 importrequire 的別名,使模塊引入更加簡(jiǎn)單。

使用配置:

// webpack.common.js

module.exports = {
  entry: './src/index.js',
+ resolve: {
+   alias: {
+     '@' : path.resolve(__dirname, 'src/')
+   }
+ }
  // 省略其他
}

alias 參數(shù)的含義:

使用 @ 來(lái)替代 path.resolve(__dirname, 'src/') 這個(gè)路徑,接下來(lái)我們測(cè)試看看。

我們?cè)?src/ 目錄下新增 leo.js

// leo.js

export let name = 'pingan';

再到 src/index.js 中引入:

// index.js

import { name } from '@/leo.js';

這樣就能正常引入。

當(dāng)然,我們也可以根據(jù)實(shí)際情況,為不同路徑設(shè)置不同別名

// webpack.common.js


alias: {
  '@' : path.resolve(__dirname, 'src/')
+ 'assets' : path.resolve(__dirname, 'src/assets/')
}

更多參數(shù)介紹,可訪問(wèn)中文官網(wǎng)的介紹:
《resolve》

2. resolve.extensions

resolve.extensions 用來(lái)自動(dòng)解析確定的擴(kuò)展,讓我們?cè)谝肽K的時(shí)候,可以不用設(shè)置拓展名,默認(rèn)值為:

extensions: [".js", ".json"]

使用配置:

// webpack.common.js

import { name } from '@/leo';

十七、 webpack 配置外部拓展

當(dāng)我們使用 CDN 引入 jquery 時(shí),我們并不想把它也打包到項(xiàng)目中,我們就可以配置 externals 外部拓展的選項(xiàng),來(lái)將這些不需要打包的模塊從輸出的 bundle 中排除:

<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>

配置 externals

// webpack.common.js

module.exports = {
  // ... 省略其他
+ externals: {
+   jquery: 'jQuery'
+ },
}

通過(guò)上面配置,我們就不會(huì)把不需要打包的模塊打包進(jìn)來(lái)。并且下面代碼正常運(yùn)行:

import $ from 'jquery';

$('.leo').show();

更多參數(shù)介紹,可訪問(wèn)中文官網(wǎng)的介紹:
《externals》

十八、 webpack 打包分析報(bào)表及優(yōu)化總結(jié)

1. 生成報(bào)表

這里我們使用 webpack-bundle-analyzer 插件,來(lái)對(duì)打包后的文件進(jìn)行數(shù)據(jù)分析,從來(lái)找到項(xiàng)目?jī)?yōu)化的方向。

webpack-bundle-analyzer 使用交互式可縮放樹(shù)形圖可視化 webpack 輸出文件的大小。

安裝插件:

npm install webpack-bundle-analyzer --save-dev

這個(gè)我們只有在開(kāi)發(fā)環(huán)境中使用。

使用插件:

// webpack.dev.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
    // ...
  ]
}

配置完成以后,我們執(zhí)行 npm run build 打包,打包完成后,會(huì)自動(dòng)打開(kāi)一個(gè)數(shù)據(jù)報(bào)表分析的頁(yè)面,地址是 http://127.0.0.1:8888/

webpack14

webpack-bundle-analyzer 將幫助我們:

  • 看清楚我們包內(nèi)都包含什么模塊;
  • 準(zhǔn)確看出每個(gè)模塊的組成;
  • 最后優(yōu)化它!

我們經(jīng)常將報(bào)表中區(qū)域最大的模塊進(jìn)行優(yōu)化!

2. 通過(guò)報(bào)表優(yōu)化項(xiàng)目

webpack14

我們可以看出,打包后的項(xiàng)目中 lodash.js 占了非常大的內(nèi)存,我們就針對(duì) lodash.js 進(jìn)行優(yōu)化。

我們將 lodash.js 改為 CDN 引入:

// index.html

<script src="https://cdn.bootcss.com/lodash.js/4.17.15/lodash.js"></script>

然后去設(shè)置上一節(jié)講到的 externals

// webpack.common.js

externals: {
  jquery: 'jQuery',
+ lodash: '_'
},

再打包以后,可以看到 lodash.js 已經(jīng)不在包里面了:

webpack15

并且打包后的文件,也能正常運(yùn)行:

webpack16

更多參數(shù)介紹,可訪問(wèn)中文官網(wǎng)的介紹:
《webpack-bundle-analyzer》

參考資料

總結(jié)

本文是根據(jù) 《2019最新Webpack4.0教程4.x 成仙之路》 學(xué)習(xí)總結(jié)下來(lái)的學(xué)習(xí)之路,適合入門(mén),涉及范圍較多,內(nèi)容比較長(zhǎng),需要能靜下心來(lái)學(xué)習(xí)。

內(nèi)容如果有誤,歡迎留言指點(diǎn),我會(huì)及時(shí)修改。

本文代碼最終托管在我的 github 上,點(diǎn)擊查看(https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Webpack/introduction/README.md)。

希望自己的文章會(huì)對(duì)各位有所幫助,也歡迎各位大佬指點(diǎn)。

Author 王平安
E-mail pingan8787@qq.com
博 客 www.pingan8787.com
微 信 pingan8787
每日文章推薦 https://github.com/pingan8787/Leo_Reading/issues
ES小冊(cè) js.pingan8787.com

微信公眾號(hào)

bg
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 本文主要從webpack4.x入手,會(huì)對(duì)平時(shí)常用的Webpack配置一一講解,各個(gè)功能點(diǎn)都有對(duì)應(yīng)的詳細(xì)例子,...
    BetterChen閱讀 2,035評(píng)論 0 3
  • 目錄第1章 webpack簡(jiǎn)介 11.1 webpack是什么? 11.2 官網(wǎng)地址 21.3 為什么使用 web...
    lemonzoey閱讀 1,821評(píng)論 0 1
  • 在現(xiàn)在的前端開(kāi)發(fā)中,前后端分離、模塊化開(kāi)發(fā)、版本控制、文件合并與壓縮、mock數(shù)據(jù)等等一些原本后端的思想開(kāi)始...
    Charlot閱讀 5,659評(píng)論 1 32
  • 鍵盤(pán)上打出“軟弱”二字一出來(lái),手機(jī)輸入法第一個(gè)就是手指向下的表情。以前看過(guò)劉同的書(shū)《向著光亮那方》,里面有一篇文章...
    葉萍閱讀 703評(píng)論 1 12
  • 我不敢相信這就是我的人生...這是戴安.阮在地震后的一段時(shí)間和馬男在一起時(shí)說(shuō)的一句話... 當(dāng)然,我沒(méi)住在洛杉磯,...
    MIKE_CHAN閱讀 273評(píng)論 0 0

友情鏈接更多精彩內(nèi)容