【Cute-Webpack】Webpack4 入門手冊(共 18 章)

介紹

1. 背景

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

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

2. 文章概要

我將從最基礎(chǔ)的【項(xiàng)目初始化】開始介紹宁炫,到【處理 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           // 存放入口文件等開發(fā)文件
+ │  └─index.js
+ ├─webpack.config.js  // webpack的配置文件

安裝 lodash

npm install lodash --save-dev

--save 可以簡寫為-S, --save-dev可以簡寫為-D.

開發(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());

開發(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. 打包測試

開始第一次打包任務(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,打開瀏覽器測試:

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

二搅吁、 webpack 處理 CSS 模塊

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

1. 修改代碼

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

  ├─package.json
  ├─dist          // 存放最終打包的文件
  │  └─index.html
  ├─src           // 存放入口文件等開發(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'; 引入我們的樣式文件吸申,是沒辦法解析使用梗劫,這時(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:對應(yīng)處理的 loader 插件名稱(處理順序是從右往左)。

3. 打包測試

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í)候可以看見 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 模塊支持如下語句:

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

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

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

2. 常用模塊

2.1 module.noParse

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

防止 webpack 解析那些符合匹配條件的文件稠氮,忽略的文件夾中不應(yīng)該含有 import曹阔、requiredefine的調(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í),匹配請求的規(guī)則數(shù)組抓韩。按照規(guī)則為對應(yīng)模塊使用對應(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ǎ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 重新打包火脉,并打開 dist/index.html 可以看到背景顏色已經(jīng)添加上去:

webpack03

4. 添加快捷打包命令

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

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

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

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

四犯戏、 webpack 開啟 SourceMap 和添加 CSS3 前綴

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

1. 開啟 SourceMap

css-loadersass-loader 都可以通過設(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

這樣我們在開發(fā)過程中,調(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ǔ)全,還支持嵌套語法送滞。

  • precss 類似 scss 語法侠草,如果我們只需要使用嵌套,就可以用它替換 scss犁嗅。

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

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

開始添加 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"
  ]
}

為了做測試宠蚂,我們修改 src/style/leo.scss.box 的樣式:

// src/style/leo.scss

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

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

webpack05

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

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

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

webpack4 開始使用 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 沒有發(fā)生變化則不壓縮;
parallel:是否啟用并行壓縮含长;
sourceMap:是否啟用 sourceMap券腔;

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

webpack07

七纷纫、webpack 為文件名添加 hash 值

由于我們打包出來的 cssjs 文件是靜態(tài)文件陪腌,就存在緩存問題辱魁,因此我們可以給文件名添加 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/

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

webpack09

八、 webpack 清理目錄插件

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

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

這里我們使用 clean-webpack-plugin 插件來實(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ǎ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 插件來處理文件導(dǎo)入的問題。

安裝插件:

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)一步铡恕,我們可以對圖片進(jìn)行壓縮和優(yōu)化琢感,這里我們用到 image-webpack-loader 插件來處理。

安裝插件:

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ǎng)的介紹:
《image-webpack-loader》

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

webpack11

十诀艰、 webpack 圖片 base64 和字體處理

1. 圖片 base64 處理

url-loader 功能類似于 file-loader柬甥,可以將 url 地址對應(yīng)的文件,打包成 base64 的 DataURL其垄,提高訪問效率苛蒲。

安裝插件:

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ǎng)的介紹:
《url-loader》

2. 字體處理

字體處理的方式和圖片處理方式是一樣的喇颁,只是我們在配置 rules 時(shí)的 test 值不相同:

// webpack.config.js

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

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

在開發(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),為了方便谭溉,我們將原來 webpack.config.js 文件修改名稱為 webpack.commen.js墙懂,并復(fù)制兩份相同的文件出來,分別修改文件名為 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 開發(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

同樣對于生產(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. 開啟 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. 測試 sourceMap

為了測試是否成功拣挪,我們將 src/index.js 代碼中擦酌,在第 12 行上,添加一句日志打印菠劝。

// src/index.js

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

對比下開啟 sourceMap 前后的區(qū)別:

webpack12

3. 開啟監(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",

這里僅對開發(fā)環(huán)境開啟锌唾,生產(chǎn)環(huán)境不需要使用锄码。

十三夺英、 webpack 熱更新

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

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

1. 開啟熱更新

插件安裝:

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)的以來
    new webpack.HotModuleReplacementPlugin() // 替換插件
 ]
}

啟動(dòng)熱更新:

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

常用配置:

contentBase: path.join(__dirname, 'dist'), //本地服務(wù)器所加載的頁面所在的目錄
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, // 啟用“打開”后厅翔,dev服務(wù)器將打開瀏覽器乖坠。
proxy: {   // 設(shè)置代理
    "/api": {
        target: "http://localhost:3000",
        pathRewrite: {"^/api" : ""}
    }
}

這時(shí)候我們訪問 http://localhost:1200/main.html 就可以看到頁面,并且修改文件刀闷,頁面也會(huì)同時(shí)刷新熊泵。

2. 優(yōu)化命令

我們可以將 npx webpack-dev-server --config webpack.dev.js 寫到 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é)涩赢,接下來給 webpack 設(shè)置代理服務(wù)器:

// webpack.dev.js

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

最后當(dā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 來提高打包效率:

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

十五武氓、 webpack 開啟 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 來配置模塊解析方式冤议。

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

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

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

1. resolve.alias

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

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

使用配置:

// webpack.common.js

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

alias 參數(shù)的含義:

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

我們在 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ǎng)的介紹:
《resolve》

2. resolve.extensions

resolve.extensions 用來自動(dòng)解析確定的擴(kuò)展艇挨,讓我們在引入模塊的時(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)脉漏,來將這些不需要打包的模塊從輸出的 bundle 中排除:

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

配置 externals

// webpack.common.js

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

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

import $ from 'jquery';

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

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

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

1. 生成報(bào)表

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

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

安裝插件:

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

這個(gè)我們只有在開發(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)打開一個(gè)數(shù)據(jù)報(bào)表分析的頁面老翘,地址是 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. 通過報(bào)表優(yōu)化項(xiàng)目

webpack14

我們可以看出汽纠,打包后的項(xiàng)目中 lodash.js 占了非常大的內(nèi)存,我們就針對 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ǎng)的介紹:
《webpack-bundle-analyzer》

參考資料

總結(jié)

本文是根據(jù) 《2019最新Webpack4.0教程4.x 成仙之路》 學(xué)習(xí)總結(jié)下來的學(xué)習(xí)之路碴犬,適合入門,涉及范圍較多梆暮,內(nè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ì)對各位有所幫助,也歡迎各位大佬指點(diǎn)贪嫂。

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

微信公眾號(hào)

bg
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末寺庄,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子撩荣,更是在濱河造成了極大的恐慌铣揉,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件餐曹,死亡現(xiàn)場離奇詭異逛拱,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)台猴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門朽合,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人饱狂,你說我怎么就攤上這事曹步。” “怎么了休讳?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵讲婚,是天一觀的道長。 經(jīng)常有香客問我俊柔,道長筹麸,這世上最難降的妖魔是什么活合? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮物赶,結(jié)果婚禮上白指,老公的妹妹穿的比我還像新娘。我一直安慰自己酵紫,他們只是感情好告嘲,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著奖地,像睡著了一般橄唬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鹉动,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天轧坎,我揣著相機(jī)與錄音,去河邊找鬼泽示。 笑死缸血,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的械筛。 我是一名探鬼主播捎泻,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼埋哟!你這毒婦竟也來了笆豁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬榮一對情侶失蹤赤赊,失蹤者是張志新(化名)和其女友劉穎闯狱,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抛计,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡哄孤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吹截。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瘦陈。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖波俄,靈堂內(nèi)的尸體忽然破棺而出晨逝,到底是詐尸還是另有隱情,我是刑警寧澤懦铺,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布捉貌,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏昏翰。R本人自食惡果不足惜苍匆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望棚菊。 院中可真熱鬧,春花似錦叔汁、人聲如沸统求。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽码邻。三九已至,卻和暖如春另假,著一層夾襖步出監(jiān)牢的瞬間像屋,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來泰國打工边篮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留己莺,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓戈轿,卻偏偏與公主長得像凌受,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子思杯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

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