webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具

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

下面有些核心概念,需要理解和掌握:

# 二、核心概念

# 入口(entry)

入口起点(entry point) 指示 webpack 应该使用哪个模块,作为构建其内部依赖的开始

entry 可以是相对路径,也可以是绝对路径

默认值是 ./src/index.js,也可以通过 webpack config.js中配置entry 属性,来指定一个(或多个)不同的入口起点,如:

// webpack.config.js
module.exports = {
  entry: './path/to/my/entry/file.js',
};

# 单个入口(简写)语法

entry 属性的单个入口语法,是以下形式的简写:

module.exports = {
  entry: {
    main: './path/to/my/entry/file.js',
  },
};

多入口写法:

module.exports = {
  entry: ['./src/file_1.js', './src/file_2.js'],
  output: {
    filename: 'bundle.js',
  },
};

# 对象语法

用法:entry: { <entryChunkName> string | [string] } | {}

module.exports = {
  entry: {
    app: './src/app.js',
    adminApp: './src/adminApp.js',
  },
};

对象语法会比较繁琐。然而,这是应用程序中定义入口的最可扩展的方式。

“webpack 配置的可扩展” 是指,这些配置可以重复使用,并且可以与其他配置组合使用

当你通过插件生成入口时,你可以传递空对象 {}entry

描述入口的对象

用于描述入口的对象。你可以使用如下属性:

  • dependOn: 当前入口所依赖的入口。它们必须在该入口被加载前被加载。
  • filename: 指定要输出的文件名称。
  • import: 启动时需加载的模块。
  • library: 指定 library 选项,为当前 entry 构建一个 library。
  • runtime: 运行时 chunk 的名字。如果设置了,就会创建一个新的运行时 chunk。在 webpack 5.43.0 之后可将其设为 false 以避免一个新的运行时 chunk。
  • publicPath: 当该入口的输出文件在浏览器中被引用时,为它们指定一个公共 URL 地址。请查看 output.publicPath (opens new window)
module.exports = {
  entry: {
    a2: 'dependingfile.js',
    b2: {
      dependOn: 'a2',
      import: './src/app.js',
    },
  },
};

确保 runtime 不能指向已存在的入口名称,runtimedependOn 不应在同一个入口上同时使用,另外 dependOn 不能是循环引用的

下面的例子都是会出现错误的:

module.exports = {
  entry: {
    a2: './a',
    b2: {
      runtime: 'x2',
      dependOn: 'a2',
      import: './b',
    },
  },
};

module.exports = {
  entry: {
    a1: './a',
    b1: {
      runtime: 'a1',
      import: './b',
    },
  },
};

module.exports = {
  entry: {
    a3: {
      import: './a',
      dependOn: 'b3',
    },
    b3: {
      import: './b',
      dependOn: 'a3',
    },
  },
};

# 常见场景

  1. 分离 app(应用程序) 和 vendor(第三方库) 入口

  2. 多页面应用程序

# 输出(output)

output 属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是 ./dist/main.js,其他生成文件默认放置在 ./dist 文件夹中。

output 必须是绝对路径

可以通过在配置中指定一个 output 字段,来配置这些处理过程:

const path = require('path'); // 用于操作文件路径

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

# loader

webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力

loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效 模块 (opens new window),以供应用程序使用,以及被添加到依赖图中。

loader:大致分为三类

  1. 编译转换
  2. 文件操作
  3. 代码检查

webpack 的其中一个强大的特性就是能通过 import 导入任何类型的模块(例如 .css 文件),其他打包程序或任务执行器的可能并不支持。

在更高层面,在 webpack 的配置中,loader 有两个属性:

  1. test 属性,识别出哪些文件会被转换。
  2. use 属性,定义出在进行转换时,应该使用哪个 loader。
const path = require('path');

module.exports = {
  output: {
    filename: 'my-first-webpack.bundle.js',
  },
  module: { // 一个单独的 module 对象
    rules: [{ test: /\.txt$/, use: 'raw-loader' }],
  },
};

loader:css样式编译(注意loader顺序,css-loader第一个执行)

  • 依赖:
> npm install css-loader --save-dev
> npm install style/core --save-dev
  • 配置:
  module: {
    rules: [
      {
        test: /.css$/, // 匹配文件
        use: [// use指定使用到的loader
          'style-loader', // 将css-loader转换后的结果放到style标签里面
          'css-loader'    // 先执行css-loader,而且是从后往前执行,所以需要放到下面 
        ]
      }
    ]
  }

loader:编译ES6+ => babel

  • 依赖:
> npm install babel-loader --save-dev         => babel 转换平台
> npm install @babel/core --save-dev          => babel 核心模块
> npm install @babel/preset-env --save-dev    => babel 转换语言包,env表示es6+全量包
  • 配置:
  1. babel-loader:
{// 转换js代码,es6+=>es5
    test: /\.js$/,
    exclude: /node_modules/,
    use: 'babel-loader'
},
  1. babel.config.js(配置可以提取出来:.babelrc、.babelrc.js、babel.config.js、package.json 文件):
// .babelrc 只会影响本项目中的代码;babel.config.js 会影响整个项目中的代码,包含node_modules中的代码
// 推荐使用:babel.config.js
module.exports = {
  presets: [
    '@babel/preset-env'
  ]
}

loader:文件/图片处理

  • 依赖:
> npm install file-loader --save-dev
> npm install url-loader --save-dev
  • 功能:
  1. file-loader 普通处理
{// 将图片文件复制到另一个目录
    test: /\.(png|svg|jpg|gif)$/,
    loader: 'file-loader',
    options: {
        name: '[name]-[contenthash:8].[ext]',
        outputPath: 'images',
        esModule: false // 新版loader需要配置,否则会产生错误:img src=[object Module]
    },
},
  1. url-loader 和 file-loader 二选一 url-loader 对 file-loader 有依赖,需要提前安装; url-loader 将图片编码为 Base64 文件:对于小文件座大小限制进行处理,减少请求次数。
{// 将小于10kb的图片编码成base64
    test: /\.(png|svg|jpg|gif)$/,
    use: {
        loader: 'url-loader',  // url-loader对file-loader有依赖,需要提前安装
        options: {
            name: '[name]-[contenthash:8].[ext]',
            outputPath: 'images',
            limit: 10 * 1024,  // 10 KB 对文件大小进行限制,超过就不转换
            esModule: false    // 新版loader需要配置,否则会产生错误:img src=[object Module]
        },
    }
}

loader:html处理(用得少,使用其他插件替代)

依赖:

> npm install html-loader --save-dev

# 插件(plugin)

loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化资源管理注入环境变量

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

const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack'); // 用于访问内置插件

module.exports = {
  module: {
    rules: [{ test: /\.txt$/, use: 'raw-loader' }],
  },
  plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
};

# 模式(mode)

可以选: development, productionnone 之中的一个,来设置 mode 参数,其默认值为 production

  • production 生产模式下,Webpack 会自动优化打包结果;(例如:代码的压缩混淆等)
  • development 开发模式下,Webpack 会自动优化打包速度,添加一些调试过程中的辅助
  • none 模式下,Webpack 就是运行最原始的打包,不做任何额外处理
module.exports = {
  mode: 'production',
};

# 依赖图(dependency graph)

每当一个文件依赖另一个文件时,webpack 都会将文件视为直接存在 依赖关系。这使得 webpack 可以获取非代码资源,如 images 或 web 字体等。并会把它们作为 依赖 提供给应用程序。

当 webpack 处理应用程序时,它会根据命令行参数中或配置文件中定义的模块列表开始处理。 从 入口 (opens new window) 开始,webpack 会递归的构建一个 依赖关系图,这个依赖图包含着应用程序中所需的每个模块,然后将所有模块打包为少量的 bundle —— 通常只有一个 —— 可由浏览器加载。

# 浏览器兼容性(browser compatibility)

Webpack 支持所有符合 ES5 标准 (opens new window) 的浏览器

# 环境(environment)

Webpack 5 运行于 Node.js v10.13.0+ 的版本。

# 二、webpack的尝试

# 2、webpack作用

把静态模块内容,压缩、转译、打包等(前端工程化)

  • 把 less/sass 转成 css 代码
  • 把 ES6+ 降级成 ES5 等
  • 支持多种模块文件类型,多种模块标准语法

# 3、体验Webpack 打包 2 个 JS 文件内容

# 3.1 目标

封装 utils 包,校验手机号和验证码长度,在 src/index.js 中使用,体验使用 Webpack 打包

# 3.2 实现步骤

新建项目文件夹 Webpack_study,初始化包环境,得到 package.json 文件

npm init -y

新建 src文件夹(书写代码)包括 utils/check.js 封装用户名和密码长度函数,引入到 src/index.js 进行使用

image-20250124100837258

src/utils/check.js

// 封装校验手机号长度和校验验证码长度的函数
export const checkPhone = phone => phone.length === 11
export const checkCode = code => code.length === 6

src/index.js

import { checkPhone, checkCode } from './utils/check.js'
console.log(checkPhone('13900002020'))
console.log(checkCode('123123123123'))

下载 webpack webpack-cli 到项目(版本独立)

npm i webpack webpack-cli --save-dev

配置package.json

"scripts":{
	"build": "webpack"
}

image-20250124101018642

项目中运行工具命令,采用自定义命令的方式(局部命令)

npm run build

自动产生 dist 分发文件夹(压缩和优化后,用于最终运行的代码)

注意:Webpack 默认入口和出口分别为:

src/index.js 和 dist/main.js

image-20250124101124102

# 三、Webpack 修改入口和出口

项目根目录,新建 Webpack.config.js 配置文件

导出配置对象,配置入口、出口文件路径(别忘了修改磁盘文件夹和文件的名字)

const path = require('path')
 
module.exports = {
  entry: path.resolve(__dirname, 'src/login/index.js'),
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: './login/index.js'  
  }
}

重新打包观察

npm run build

# 四、案例练习

  1. 初始化项目结构

目录结构:

my-webpack-project/
├── src/
│   ├── assets/
│   │   ├── images/
│   │   │   └── logo.png
│   ├── styles/
│   │   ├── main.scss
│   │   └── variables.less
│   ├── index.js
│   └── index.html
├── dist/
├── package.json
├── webpack.config.js
└── .babelrc
  1. 初始化项目
npm init -y
  1. 安装依赖
npm install --save-dev webpack webpack-cli webpack-dev-server html-webpack-plugin mini-css-extract-plugin css-loader sass-loader node-sass less-loader style-loader babel-loader @babel/core @babel/preset-env file-loader url-loader
  1. src/index.html 内容
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Webpack Project</title>
</head>
<body>
    <div id="app"></div>
</body>
</html>
  1. src/index.js 内容
import "./styles/main.scss";
import logo from "./assets/images/logo.png";

const img = document.createElement("img");
img.src = logo;
img.alt = "Logo";
img.style = "width: 200px; height: auto;";

document.getElementById("app").appendChild(img);

console.log("Webpack setup successful!");
  1. src/styles/main.scss 内容
$primary-color: #3498db;
@import "./variables.less";

body {
    font-family: Arial, sans-serif;
    background-color: $primary-color;
    color: @secondary-color;
    margin: 0;
    padding: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
}
  1. src/styles/variables.less 内容
@secondary-color: #2ecc71;
  1. Webpack 配置 (webpack.config.js)
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
    entry: "./src/index.js",
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: "bundle.js",
        clean: true, // 自动清理 dist 文件夹
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: "babel-loader",
                    options: {
                        presets: ["@babel/preset-env"],
                    },
                },
            },
            {
                test: /\.(css|scss|sass)$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    "css-loader",
                    "sass-loader",
                ],
            },
            {
                test: /\.less$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    "css-loader",
                    "less-loader",
                ],
            },
            {
                test: /\.(png|jpg|gif|svg)$/,
                type: "asset/resource",
            },
        ],
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: "./src/index.html",
        }),
        new MiniCssExtractPlugin({
            filename: "styles.css",
        }),
    ],
    devServer: {
        static: path.resolve(__dirname, "dist"),
        port: 8080,
        open: true,
    },
    mode: "development", // 或 production
};
  1. .babelrc 文件内容
{
    "presets": ["@babel/preset-env"]
}
  1. npm 脚本 (package.json)
"scripts": {
    "start": "webpack serve",
    "build": "webpack"
}

使用说明:

  • npm start: 启动开发服务器.
  • npm run build: 打包生产环境代码.

# Vite 知识点与使用

# Vite 简介

Vite 是一个现代的前端构建工具,专注于快速开发和构建。相比 Webpack,Vite 的特点是:

  1. 快速冷启动:使用原生 ES 模块,省去了传统打包工具的模块预处理步骤。
  2. 即时热更新(HMR):基于浏览器原生功能,速度更快。
  3. 简单配置:默认配置开箱即用,支持现代框架如 Vue、React。
  4. 生产环境优化:基于 Rollup 构建,生成高效的生产环境代码。

# Vite 与 Webpack 的比较

特性 Vite Webpack
构建速度 快速冷启动,适合开发环境 初次构建较慢,后续有增量优化
热更新(HMR) 高效,依赖 ES 模块 较慢,需重新打包部分模块
配置复杂度 简单,开箱即用 灵活但较复杂
生产环境打包 使用 Rollup,构建高效 自身提供,功能丰富
社区生态 新兴但快速增长 成熟且广泛

# 使用 Vite 创建项目

# 1. 初始化 Vite 项目

npm create vite@latest my-vite-project
cd my-vite-project
npm install

# 2. Vite 项目目录结构

my-vite-project/
├── public/        # 静态资源目录
├── src/           # 源码目录
│   ├── assets/    # 资源文件
│   ├── main.js    # 入口文件
│   ├── App.vue    # Vue 文件(Vue 项目示例)
├── index.html     # 主 HTML 文件
├── package.json   # 项目信息
├── vite.config.js # Vite 配置文件

# 3. 安装插件

根据需要安装插件,如 Vue、React 支持:

npm install @vitejs/plugin-vue

# 4. 启动开发服务器

npm run dev

# 5. 打包生产环境代码

npm run build

# Vite 配置文件 (vite.config.js)

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";

export default defineConfig({
    plugins: [vue()],
    server: {
        port: 3000,
        open: true,
    },
    build: {
        outDir: "dist",
        sourcemap: true,
    },
});

# 总结

  • Vite 更适合快速开发,特别是针对 Vue、React 等现代框架。
  • Webpack 更适合需要复杂自定义的构建场景。
  • 学习两者可以灵活应对各种项目需求。