Skip to content

Webpack基础

一、什么是Webpack

Webpack 是一个用于构建前端应用的工具,它将多个模块打包成一个或多个文件,并提供了代码拆分、懒加载、模块热替换等功能。

webpack 的模块是什么

Webpack 忽略所有资源之间的差异,将所有代码/非代码文件都统一看作 Module —— 模块对象。

这些模块都以相同的加载、解析、依赖管理、优化、合并流程实现打包,就像 webpack 官网所示这样。

webpack

并借助 Loader、Plugin 两种开放接口将资源差异处理逻辑转交由社区实现,实现统一资源构建模型

这种设计有很多优点:

  • 所有资源都是 Module,所以可以用同一套代码实现诸多特性,包括:代码压缩、Hot Module Replacement、缓存等
  • 打包时,资源与资源之间非常容易实现信息互换,例如可以轻易在 HTML 插入 Base64 格式的图片
  • 借助 Loader,Webpack 几乎可以用任意方式处理任意类型的资源,例如可以用 Less、Stylus、Sass 等预编译 CSS 代码

为什么要模块打包呢?

1. 浏览器不识别CommonJS等模块化规范

要知道node.js 生态中积累了大量的 JavaScript 写的代码,却因为 node.js 端遵循的 CommonJS 模块化规范与浏览器端格格不入,导致代码无法得到复用,这是一个巨大的损失。于是 webpack 要做的就是将这些模块打包成游览器识别的代码。

2. 依赖引入顺序问题

在不使用webpack的情况下,我们引入JS文件是这样的

js
// index.html
<script src="index.js"></script>

但是如果一个页面需要引入多个模块,那么就需要写成这样

js
// index.html
<script src="index1.js"></script>
<script src="index2.js"></script>
<script src="index3.js"></script>

虽然这样引入初看还可以,但是事实却隐藏着一个问题,那就是如何保证依赖执行的顺序正确?

如果 index1.js 使用到了 index2.js 的内容就会报错。这时因为JS执行时从上到下的,所以在执行 index1.js index2.js还没执行,里面的内容也还没有。

webpack 如何识别模块关系

  1. 首先 webpack 会从你配置的入口文件开始
  2. 寻找当前文件所依赖的模块
  3. 查找 2 中导入模块的依赖,进行遍历递归,重复 1 2 3 顺序执行
  4. 直到查找完全部模块,生成依赖图 webpack

二、webpack 工作流程

webpack 的工作流程可以概括为下面四个步骤: webpack

其中 Loader 主要功能是处理 webpack 获取到的模块,webpack 自身只会识别 js 模块。所以各种 webpack 不能识别的资源都将交给 loader 处理。

而 Plugin 则是对于 Loader 处理出来的资源进行进一步的改造,例如 代码压缩、生成 HTML 文件等。

最终输出所有文件和资源 boudle

三、webpack 配置

Entry 入口文件

Webpack 的入口起点是通过配置文件指定的一个或多个 JavaScript 文件,这些文件会作为打包的起点。Webpack 会根据这些入口文件来分析应用中的模块依赖关系,构建依赖图。

js
// webpack.config.js
module.exports = {
    entry: './index.js'
};

Loader 模块解析

Webpack 使用加载器(Loader)来处理非 JavaScript 和 JSON 类型的模块:将其转换成 JavaScript 模块,然后再进行打包。Webpack 支持多种类型的加载器,例如 css-loader 等。

在 webpack 的配置中,loader 有两个属性:

  1. test 属性,识别出哪些文件会被转换。
  2. use 属性,定义在进行转换时,应该使用哪个 loader。
js
// webpack.config.js
module.exports = {
    module: {
        rules: [{ test: /\.txt$/, use: 'raw-loader' }],
    },
}

以上 loader 的意思是告诉 webpack 在碰到 require()/import 语句中被解析为 '.txt' 的路径时,在你对它打包之前,先 use(使用) raw-loader 转换一下。

TIP

像 png、jpg 等基础资源的识别,webpack5 内置资源模块(asset module)。不需要安装额外的 loader模块 (file-loader 等)来提供帮助,只需要配置 type 即可 { test: /.png/, type: 'asset/resource' }。

Plugin 插件

Webpack 使用插件(Plugin)来扩展其功能,例如压缩代码、提取公共代码、生成 HTML 文件等。开发者可以编写自己的插件来满足特定需求。

HtmlWebpackPlugin(自动生成一个 HTML 文件)

安装 HtmlWebpackPlugin

npm install HtmlWebpackPlugin -D

配置 webpack.config.js

js
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
  entry: './index.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'index.js',
  },
  plugins: [new HtmlWebpackPlugin()],
}

想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建一个插件实例。

Output 输出结果

Webpack 将所有模块打包成一个或多个文件,并且可以生成 sourcemap 文件用于调试。输出的文件可以通过配置文件指定其名称、路径和格式。

js
// webpack.config.js
const path = require('path');
module.exports = {
    entry: './index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'my-first-webpack.bundle.js',
    },
};

四、其它配置补充

asset module

资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader。 在 webpack 5 之前,通常使用:

  • raw-loader 将文件导入为字符串
  • url-loader 将文件作为 data URI 内联到 bundle 中
  • file-loader 将文件发送到输出目录

资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:

  • asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
  • asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现。
  • asset/source 导出资源的源代码。之前通过使用 raw-loader 实现。
  • asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现。

例如:

js
const path = require('path')

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'main.js',
        path: path.resolve(__dirname, 'dist')
    },
    module: {
        rules: [
            {
                test: /\.png/,
                type: 'asset/resource'
            }
        ]
    },
}

所有 .png 文件都将被发送到输出目录,并且其路径将被注入到 bundle 中,除此之外,你可以为它们自定义 outputPath 和 publicPath 属性。

optimization

用于控制如何优化产物包体积,内置 Dead Code Elimination、Scope Hoisting、代码混淆、代码压缩等功能

js
module.exports = {
    //...
    optimization: {
        //...
    };  
}

详细配置请看 JS 优化。

mode

配置 webpack 的模式,有三个值:

  • development 开发模式,会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development。
  • production 生产模式,会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production。
  • none 不使用任何默认优化选项
js
module.exports = {
    mode: 'production', 
}

如果没有设置,默认为 production。在 webpack5 中使用 production 生产模式,回默认开启一些优化:例如启动 JS 代码压缩等优化。

devServer

用于拉起一个服务来启动前端项目

js
module.exports = {
    devServer: {
        contentBase: path.join(__dirname, 'dist'),
        port: 8080
    }
}
  • contentBase:告诉服务器内容的来源。
  • port:设置服务器端口,默认8080

五、认识 Module、Chunk 和 Bundle

Module
Module(模块)是Webpack中最基本的概念,它代表着一个单独的文件(或一组相关的文件),它可以是JavaScript、CSS、图片、JSON等任何类型的文件。在Webpack中,每个Module都会被转换成一个或多个Chunk。

Chunk
Chunk(代码块)是Webpack打包过程中的中间产物,它代表着一组被合并在一起的Modules。通常情况下,Chunk是由多个Module组成的,Webpack会根据一定的规则将这些Module打包成一个Chunk。Chunk可以被进一步处理和优化,最终被合并成Bundle。

Bundle
Bundle(捆绑包)是Webpack最终输出的文件,它是由一组已经经过加载和编译的Chunk组成的。通常情况下,一个应用程序会生成一个Bundle,它包含了所有的JavaScript、CSS、图片等资源,可以被直接加载到浏览器中运行。

Module是Webpack中最小的单元,它们组成了Chunk,而Chunk则最终被合并成Bundle。module,chunk 和 bundle 其实就是同一份逻辑代码在不同转换场景下的取了三个名字