webpack入门 | 青训营笔记

68 阅读9分钟

文章第一句话为“这是我参与「第四届青训营 」笔记创作活动的第3天

webpack

webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,用于展示你的内容。

我们结合官网的banner图,用大白话来解读这段定义:

2.png

  • webpack是一个打包工具,一个资源中转站。它接收一些文件,经过内部处理后,输出对应的静态资源
  • 它从入口文件出发,找到入口依赖的模块,再递归寻找依赖的依赖,直到所有模块被加载进来
  • 它把开发者使用方便但浏览器不能直接识别的资源(如less,scss,ES Next等),处理成浏览器可直接识别的资源(css,js等)

使用webpack

webpack的使用方法基本都围绕“配置”展开,这些配置大致可划分为两类: 流程类:作用于流程中某个or若干个环节,直接影响打包效果的配置项 工具类:主流程之外,提供更多工程化能力的配置项

流程类配置

image.png

配置总览

image (1).png

核心概念

接下来介绍webpack的四个核心概念:入口(entry),输出(output),loader,插件(plugins)

entry——入口

入口起点(entry point)  指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) 的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。

我们现在大多数开发的是单页面应用,这里我们以单入口为例,具体配置可去文档中查看

我们修改目录结构,新增webpack.config.js文件(注意该文件必须与src同级,且不能更改命名。后续我们可以通过更改script脚本命令来打破这些限制。)与add.js文件。

├─ src
│  ├─ add.js
│  └─ index.js
├─ .gitignore
└─ README.md
└─ webpack.config.js
└─ package.json

webpack.config.js文件配置如下,entry配置的是相对路径:

module.exports = {
  entry: './src/index.js'
}

add.js内容如下:

const add = (a,b) => {
  return a + b
}

export default add

index.js引入这个add函数并输出:

import add from './add.js'

const hello = () => {
  console.log('hello webpack')
}

hello()
console.log(add(2,3))

接下来打开终端输入webpack回车看看它是如何打包文件的。

5.png

可以看到刚刚引入的add函数也被打包输出。注意这个add函数输出的形式,webpack帮我们直接输出了函数的运算结果。

只要资源被其他模块引入并使用,webpack就会帮我们打包这些资源

output——输出

可以通过配置 output 选项,告知 webpack 如何向硬盘写入编译文件。注意,即使可以存在多个 entry 起点,但只能指定一个 output 配置。

我们更改webpack.config.js的配置如下:

const path = require('path')

const resolvePath = _path => path.resolve(__dirname, _path)

module.exports = {
  entry: './src/index.js',

  output: {
    path: resolvePath('./dist'),
    filename: 'scripts/[name].js'
  }
}

这里讲解一下output中这2个属性:

  • path: 文件输出的路径,必须是个绝对路径
  • filename: 文件输出的名称。可以在名称前指定一个输出路径,让文件输出在这个路径下。同时也可以使用占位符[name]指定不同的文件名称

终端输入webpack打包,我们查看打包后的程序

6.png

新的main.js被打包到我们在filname中指定的scripts文件夹下。

这里有个新的问题待我们解决,之前被打包的main.js没有被清除,我们希望每次打包都能生成最新的文件,并清除上一次的打包结果。此时在output中配置clean:true即可解决问题,更改output配置如下:

output: {
    path: resolvePath('./dist'),
    clean: true,
    filename: 'scripts/[name].js'
  }

再次打包:

7.png

效果实现,之前的文件已被清除。这里需要注意,webpack 5.0以下的版本,需要另外引入插件clean-webpack-plugin进行配置,过程较为复杂,不展开赘述,了解即可。webpack 5.0以上仅需在output中配置clean的值为true即可实现同样的效果

loader

webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中。

用大白话翻译一下就是:loader是一个翻译,把webpack不能直接处理的资源,翻译成能直接处理的

那什么是webpack不能直接处理的资源呢?我们尝试打包一个css资源,调整目录结构如下:

├─ src
│  ├─ css
│  │  └─ index.css
│  └─ add.js
│  └─ index.js
├─ .gitignore
└─ README.md
└─ webpack.config.js
└─ package.json

index.css中添加如下样式:

body {
  background-color: red;
}

index.js引入index.css后,进行打包,查看结果如下:

8.png

报错提示: webpack不识别样式的写法,我们需要一个合适的loader来处理这种类型的文件。

这就是webpack不能直接处理的资源。也就是说除了webpack开箱自带支持的JS,JSON文件,其余文件都不能被webpack直接处理,此时就需要我们引入loader帮助webpack翻译翻译。

接下来我将围绕样式资源,图片资源等跟大家讲解loader的具体使用方法。

样式loader

加载css文件——css-loader,style-loader
  • 步骤1: 识别基础css资源需要安装2个loader

yarn add css-loader style-loader -D

loader作用如下:

css-loader:将css文件处理成commonJs模块放入js中

style-loader:创建style标签将js中的css插入到html中

  • 步骤2: 下载完成后,配置webpack.config.js使用loader
module.exports = {
  // ...

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

这里我们新增了module配置项,它用来配置不同loader的使用规则。module.rules 允许你在 webpack 配置中指定多个 loader。

rules中每一个对象即为处理一种类型文件的方式,具体字段释义如下:

test: loader匹配的规则

use:对应文件使用的loader

这里的配置是为所有css文件匹配使用style-loadercss-loader。注意loader的执行顺序是从下到上,从右到左。先执行css-loader处理css,再执行style-loader将css通过style标签插入页面。

  • 步骤3: 执行webpack查看结果:

9.png

编译成功,我们在dist根目录下新建index.html,并引入打包后的main.js查看结果:

10.png

打包后的js和样式全部生效了,我们看看样式插入的方式:

11.png

样式通过style标签被插入页面。到这里基础的样式loader使用就已经成功。但实际开发中我们多以less,scss开发为主,处理这些样式文件需要使用对应的less-loadersass-loader,这里拿less举例。

加载less文件——less-loader

我们调整src/css文件结构,新增一个less文件,如下所示:

├─ css
│  ├─ index.css
│  └─ rectangle.less

less文件样式如下:

@blue: #4285f4;

.webpack-rectangle{
  width: 100px;
  height: 60px;
  background-color: @blue;
}

src/index.js引入less文件,进行打包

import add from './add.js'
import './css/index.css'
import './css/rectangle.less'

查看结果,发现报错:

12.png

报错信息: 不支持less文件的打包,需要一个loader处理该类文件。

与css报错的信息基本一致,这时请出我们的less-loader。

  • 步骤1: 安装less,less-loader

yarn add less less-loader -D

  • 步骤2: 配置webpack.config.js
// ...
module.exports = {
  // ...
  module: {
    rules:[{
      test: /.css$/,
      use:[
        'style-loader',
        'css-loader'
      ]
    },{
      // 匹配less文件
      test: /.less$/,
      // loader的使用顺序 less-loader,css-loader,style-loader
      use:[
        'style-loader',
        'css-loader',
        'less-loader'
      ]
    }]
  },

  mode:'development'
}

这里新增mode:'development',防止webpack报一些提示信息的错误。然后在rules数组中新增处理less文件的loader对象。

这里的处理顺序如下所示:

  1. less-loader将less文件转义成css文件
  2. css-loader将css文件处理成commonJs模块
  3. style-loader将css通过style标签插入页面中
  • 步骤3: 在dist根目录下再次新建一个index.html引入main.js查看刚刚写的less是否生效,这里为了观察方便我们把原来的红色背景改成灰色背景。
<body>
  <div class="webpack-rectangle"></div>
  <script src="./scripts/main.js"></script>
</body>

13.png

资源loader

处理图片资源

webpack4中处理图片使用file-loaderurl-loader,webpack5中,这2个loader已被内置到webpack里,我们只需激活配置即可。

  • 步骤1: 调整src结构
├─ src
│  ├─ assets
│  │  └─ img.png
│  │  └─ iron-man.gif
│  │  └─ mk50.jpeg
│  ├─ css
│  │  └─ index.scss
│  └─ add.js
│  └─ index.js
  • 步骤2: 配置webpack.config.js的rules
rules:[
    // ...
    {
      test: /.(jpe?g|png|gif|webp|svg)$/,
      type: 'asset',
      generator: {
        filename: 'assets/img/[hash:10][ext]'
      }
    }]
  • 步骤3: 修改index.scss
$baseCls:"webpack-img";

.#{$baseCls} {
  display: flex;
  justify-content: space-between;
  div{
    width: 230px;
    height: 230px;
    &.box1{
      background: url(../assets/img/img.png) no-repeat center /contain;
    }
    &.box2{
      background: url(../assets/img/iron-man.gif) no-repeat center /contain;
    }
    &.box3{
      background: url(../assets/img/mk50.jpeg) no-repeat center /contain;
    }
  }
}
  • 步骤4: 执行webpack,dist根目录下创建index.html引入main.js查看样式
<body>
  <div class="webpack-img">
    <div class="box1"></div>
    <div class="box2"></div>
    <div class="box3"></div>
  </div>
  <script src="./scripts/main.js"></script>
</body>

查看结果:

15.png

成功运行,我们看看dist目录生成的文件

16.png

新生成了assets文件夹,里面包含hash名称的图片资源,再看看刚刚的loader中有这样的配置。

generator: {
    filename: 'assets/img/[hash:10][ext]'
}

这下应该明白了,filename可以指定图片资源的输出路径,其他字段释义如下:

[hash:10]:取hash的前10位为文件名称

[ext]:文件扩展名,之前是什么值,打包后仍然是什么值

可以看出,打包前和打包后,文件扩展名是未发生修改的。到这里图片资源的处理就完成了。

babel-loader

将 ES6+ 语法编写的代码转义为向下兼容的js代码,使低版本浏览器能正常运行程序。

在使用babel-loader前,调整目录结构如下:

├─ src
│  └─ index.js
├─ .gitignore
└─ README.md
└─ webpack.config.js
└─ package.json

在index.js中编写一段ES6代码:

class GirlFriend {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  say () {
    console.log(`我叫${this.name},我今年${this.age}岁。很高兴认识你`)
  }
}
const girl = new GirlFriend('Alice',22)
girl.say()

使用webpack打包查看结果:

17.png

可以发现,ES6的代码被原封不动地打包了,这样的打包结果,对低版本浏览器并不友好。所以我们需要引入babel-loader解决这个问题,下面我们开始配置babel-loader。

  • 步骤1: 安装依赖
yarn add babel-loader @babel/core @babel/preset-env -D

@babel/core :babel核心库

@babel/preset-env: 智能预设,允许开发者使用最新的js

  • 步骤2: 配置webpack.config.js的rules:
rules: [
    // ...
    
    {
      test: /.js$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env']
        }
      }
    }]

当然我们也可以在项目根目录中新增.babelrcbabel.config.js等文件,设置一些babel的使用规则。关于babel更详细地使用这里不多做展开,请查阅官方文档了解。

  • 步骤3: 执行webpack查看打包结果

18.png

19.png

可以看到我们的ES6代码已被转义为ES5代码。

plugin——插件

plugins 选项用于以各种方式自定义 webpack 构建过程。它监听webpack的打包过程,执行对应的生命周期回调,拓展webpack的功能。

啥是插件?为什么要这么设计?

这是一个特别复杂的过程,所以:1.新人需要了解整个流程细节,上手成本高,功能迭代成本高,牵一发而动全身 2.功能僵化,作为开源项目而言缺乏成长性

所以:心智成本高 => 可维护性低 => 生命力弱

插口架构设计精髓:对扩展开放,对修改封闭

打包html页面——HtmlWebpackPlugin

之前我们打包js,css后,总要在dist根目录下新建一个html,引入打包的js并进行一些设置,才能在页面中看到打包后的效果。这种方式非常不方便,因此我们通过HtmlWebpackPlugin这一插件,让webpack帮我们自动打包出一个index.html。

这里我们提一个需求,我们希望webpack能打包一个html页面,这个页面生成时带有这样的内容<h2>Hello CengFan!</h2>,并引入打包后的js资源。

需求有了接下来我们一步步实现

  • 步骤1: src文件夹下新建一个html,内容如下
<body>
  <h2>Hello Cengfan!</h2>
</body>
  • 步骤2: 安装依赖

yarn add html-webpack-plugin -D

  • 步骤2: 配置webpack.config.js
const path = require('path')
// 引入插件
const HtmlWebpackPlugin = require('html-webpack-plugin')

const resolvePath = _path => path.resolve(__dirname, _path)

module.exports = {
  // ...

  plugins: [
    new HtmlWebpackPlugin({
      template: resolvePath('./src/index.html'),
    })
  ],

  mode: 'development'
}

template指定以哪个html为入口文件,值是一个绝对路径。

plugin都要遵循先引用,然后使用new关键字进行实例化调用的原则。

  • 步骤3: 执行webpack查看打包结果

20.png

符合预期,js也正常运行

21.png

mode模式

mode主要分为开发/生产两种环境,不同环境打包出来的代码不同。

development:开发环境

production:生产环境

首先看看开发环境下打包的js代码:

25.png

再看看生产环境下打包的js代码:

26.png

生产环境下我们还可以配合其他插件,压缩生产环境的代码。

In the end

至此webpack常用的配置已列举完毕,如果你也跟着一路看下来相信基本的配置应该不成问题了。受限于篇幅,更全的配置还是推荐大家去官网查看。

最后贴一张学习路线图

image.png