# 1、webpack解决了什么问题

参考: (opens new window)

Webpack 是一个 现代 JavaScript 应用程序的静态模块打包工具(Module Bundler),它主要用于 前端项目的资源构建和依赖管理。它的核心思想是 “一切皆模块”,即 JavaScript、CSS、图片、字体等资源都可以被当作模块来处理,并通过 依赖关系 进行打包优化。

# 1、模块化开发的问题

  • 传统开发方式:
    • 使用 <script> 标签手动引入 JS 文件,依赖管理混乱。
    • 全局变量污染(如 var 定义的变量可能冲突)。
  • Webpack 的解决方案:
    • 支持 ES Modules(ESM)、CommonJS(CJS)、AMD 等模块化方案。
    • 自动分析依赖关系,按需打包。

示例:

// 传统方式(全局变量冲突)
<script src="jquery.js"></script>
<script src="app.js"></script> // 可能依赖 jQuery,但无法保证加载顺序

// Webpack 方式(模块化)
import $ from 'jquery';
$('#app').text('Hello Webpack!');
1
2
3
4
5
6
7

# 2、资源管理的问题

  • 传统方式:
    • CSS、图片、字体等资源需要手动引入,难以优化(如合并、压缩)。
    • 手动管理 CDN 或文件路径,容易出错。
  • Webpack 的解决方案:
    • 使用 Loader(如 css-loader、file-loader)处理非 JS 资源。
    • 使用 Plugin(如 MiniCssExtractPlugin)优化资源。
// webpack.config.js
module.exports = {
  module: {
    rules: [
      { test: /\.css$/, use: ['style-loader', 'css-loader'] }, // 处理 CSS
      { test: /\.(png|jpg)$/, use: ['file-loader'] }, // 处理图片
    ],
  },
};
1
2
3
4
5
6
7
8
9

# 3、性能优化的问题

  • 传统方式:
    • 所有 JS/CSS 打包成一个文件,首屏加载慢。
    • 没有 Tree Shaking(未使用的代码也被打包)。
  • Webpack 的解决方案:
    • 代码分割(Code Splitting):按需加载(如 import() 动态导入)。
    • Tree Shaking:移除未使用的代码(需 ES Modules)。
    • 缓存优化:[contenthash] 避免浏览器缓存问题。
// 动态加载(Code Splitting)
import('./module.js').then(module => {
  module.doSomething();
});

// Tree Shaking(仅打包使用的代码)
import { util1 } from 'utils'; // 如果 util2 未使用,不会打包
1
2
3
4
5
6
7

# 4、开发体验的问题

  • 传统方式:
    • 修改代码后需手动刷新浏览器。
    • 没有 Source Map,调试困难。
  • Webpack 的解决方案:
    • Hot Module Replacement(HMR):局部热更新,不刷新页面。
    • DevServer:快速启动本地开发服务器。
    • Source Map:方便调试压缩后的代码。
// webpack.config.js
devServer: {
  hot: true, // 开启 HMR
  open: true, // 自动打开浏览器
},
devtool: 'cheap-module-source-map', // 生成 Source Map
1
2
3
4
5
6

# 5、多环境支持

Webpack可以根据配置的不同,生成适合开发、生产等不同环境的代码。

例如,开发环境下可以保留调试信息,而生产环境下可以进行代码压缩和混淆。

# 2、Webpack 的构建流程分析

Webpack 的构建流程可以分为多个阶段,每个阶段都有特定的任务和钩子(Hooks)。

# 2-1、初始化阶段

  1. 读取配置文件:解析 webpack.config.js 或命令行参数,合并配置。
  2. 创建 Compiler 对象:Webpack 的核心调度器,负责整个构建流程的控制。
  3. 加载插件:执行插件的 apply 方法(如 new HtmlWebpackPlugin()),注册钩子。
  4. 确定入口(Entry):根据配置的 entry 属性找到所有入口文件。
// webpack 内部伪代码
const compiler = new Compiler(options);
plugins.forEach(plugin => plugin.apply(compiler)); // 插件初始化
compiler.run(); // 启动构建
1
2
3
4

# 2-2、编译阶段

  1. 初始化阶段:Webpack从配置文件(如webpack.config.js)读取并合并用户的配置,然后创建一个Compiler实例。这个实例中包含了所有的配置信息和钩子(生命周期函数),接下来它会开始整个构建过程。

  2. 编译阶段:Webpack会从入口点开始递归地解析每个模块的依赖关系。每找到一个新的模块,就会根据模块类型和loader规则进行处理。处理后的模块会被加入到依赖图中。
    解析入口点:Webpack根据配置找到入口文件。
    模块解析与加载:Webpack使用配置的loader处理模块,支持各种资源类型(如JS、CSS、图片等)。
    依赖关系分析:Webpack通过静态分析,识别模块中引入的依赖,并继续递归地解析这些依赖。

  3. 构建阶段:在解析完所有模块的依赖后,Webpack会开始根据依赖图将模块组合起来。这个过程包括将各个模块转换为浏览器可执行的代码(js代码),并根据需要进行优化。
    模块合并:Webpack将模块按照依赖关系打包到一个或多个文件中。
    代码分割:根据配置,将代码拆分为多个块,实现按需加载。

  4. 优化阶段:在构建完成后,Webpack会对输出的文件进行一系列优化操作。常见的优化包括: TreeShaking:移除未使用的代码。
    代码压缩:压缩JS和CSS文件,减少文件体积。
    文件指纹(Hashing):为文件生成唯一的哈希值,便于浏览器缓存管理。
    代码拆分(Splitting):将代码拆分成更小的块,按需加载。

  5. 输出阶段:根据入口文件及其依赖关系将其打包成一个或多个chunk,再把每个Chunk转换成一个单独的文件加入到指定的输出列表(seal方法生成chunk),这步是可以修改输出内容的最后机会。

  6. 完成阶段:构建流程完成,Webpack触发done钩子,根据配置确定输出的路径和文件名进行打包。可以在此阶段进行一些后续处理,如通知开发者构建完成、自动刷新浏览器等。

# 3、Loader和plugin的区别:

Loader 用于对模块的源代码进行转换
 因为 webpack 本身只能处理 JavaScript,如果要处理其他类型的文件,就需要使用 loader 进行转换。  loader 本身就是一个函数,接受源文件为参数,返回转换的结果。

Plugin 是用来扩展 Webpack 功能的
 Plugin 可以完成 Loader 所不能完成的复杂功能,使用 plugin 丰富的自定义 API 以及生命周期事件。
 可以控制 webpack 打包流程的每个环节,实现对 webpack 的自定义功能扩展。

 Loader 在 module.rules 中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(类型文件)、loader、options (参数)等属性。
 Plugin 在 plugins 中单独配置,类型为数组,每一项是一个 Plugin 的实例,参数都通过构造函数传入。

常见的Loader:
 Limage-loader:加载并且压缩图片文件
 Lsass-loader:将SCSS/SASS代码转换成CSS
 Lbabel-loader:把 ES6 转换成 ES5
 Lvue-loader:加载 Vue.js 单文件组件
 Lts-loader: 将 TypeScript 转换成 JavaScript

常见的Plugin:
 Lhtml-webpack-plugin:简化 HTML 文件创建
 Lmini-css-extract-plugin:将CSS提取为独立的文件  
 LHotModuleReplacementPlugin:热更新

# 4、source map 以及生产环境怎么使用

 Lsource map 是将编译、打包、压缩后的代码映射回源代码的过程
 Lmap文件只要不打开开发者工具,浏览器是不会加载的

线上环境一般有三种处理方案:
 Lhidden-source-map:借助第三方错误监控平台 Sentry 使用
 Lnosources-source-map:只会显示具体行数以及查看源代码的错误栈。安全性比 sourcemap 高
 Lsourcemap:通过 nginx 设置将 .map 文件只对白名单开放(公司内网)

# 5、CSS打包

# 5-1、普通打包:

npm i style-loader css-loader -D
1

tip:以行内样式style的标签写进打包后的html页面中

module: { 
    rules: [ 
        { 
            test: /\.css$/, // 解析css 
            use: ['style-loader', 'css-loader'] // 从右向左解析 
        /* 也可以这样写,这种方式方便写一些配置参数 
            use: [ 
                {loader: 'style-loader'}, 
                {loader: 'css-loader'} 
            ] 
        */ 
        } 
    ] 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 5-2、拆分css

npm i css-loader -D
1

tip:以link的方式引入,需要 extract-text-webpack-plugin插件

// @next表示可以支持webpack4版本的插件

npm i extract-text-webpack-plugin@next -D
1
module: {
    rules: [
        {
            test: /\.css$/,
            use: ExtractTextWebpackPlugin.extract({
                // 将css用link的方式引入就不再需要style-loader了
                use: 'css-loader'       
            })
        }
    ]
},
1
2
3
4
5
6
7
8
9
10
11

# 6、JS打包

# 6-1、多个js文件不合并打包(分别打包)

entry: {
	main1:"./src/main1",
	main2:"./src/main2"
}, 
output: { 
    path: path.resolve(__dirname, "../dist/js"), //将js文件打包到dist/js的目录
    filename: "[name].js" //使用[name]打包出来的js文件会分别按照入口文件配置的属性来命名
}
1
2
3
4
5
6
7
8

# 6-2、多个js中部分合并打包成一个js文件

entry: {
	main1:"./src/main1",
	main:["./src/main2","./src/main3"]
},
output: { 
    path: path.resolve(__dirname, "../dist/js"), //将js文件打包到dist/js的目录
    filename: "[name].js" //使用[name]打包出来的js文件会分别按照入口文件配置的属性来命名
}
1
2
3
4
5
6
7
8

# 6-3、多个js全部打包成一个js文件

entry: ["./src/main1","./src/main2","./src/main3"], 
output: { 
    path: path.resolve(__dirname, "../dist/js")
    filename: "main.js" 
1
2
3
4

# 7、vite 和 webpack 的区别

# 7-1、构建模式

特性 Webpack Vite
打包方式 打包器(Bundle-based)(将所有资源打包成少数几个文件) 原生 ESM(No-Bundle)(直接利用浏览器原生 ES Modules)
开发启动速度 慢(需打包整个应用) 极快(按需编译,无需打包)
生产构建 仍使用打包优化(Tree Shaking、Code Splitting) 使用 Rollup 进行生产打包

关键区别:

  • Webpack 是 打包器,开发和生产环境都需打包。
  • Vite 开发环境 不打包,直接利用浏览器 ESM,生产环境用 Rollup 打包。

浏览器 ESM:现代模块化标准,适合开发环境快速迭代。
Rollup:生产环境打包工具,专为 ESM 优化,适合库和应用发布

# 7-2、开发体验

特性 Webpack Vite
冷启动 慢(需构建整个依赖图) 秒级启动(仅启动开发服务器)
HMR(热更新) 较慢(需重新打包变动的模块) 毫秒级更新(直接替换变动的 ESM 模块)
调试体验 需 Source Map 支持 原生 ESM,代码几乎未编译,调试方便
  • 一个大型项目:
    • Webpack 启动可能需要 10s~1min(依赖越多越慢)。
    • Vite 启动通常 <1s(仅启动服务器,按需编译)。

# 7-3、适用场景

工具 适用场景 不适用场景
Webpack - 传统大型项目
- 需要高度自定义打包(如微前端、特殊资源处理)
- 依赖老旧库(如 jQuery)
追求极速开发的现代项目
Vite - 现代前端项目(Vue/React/Svelte)
- 需要快速启动和 HMR
- 想减少配置复杂度
需要兼容 IE11 或特殊打包需求
lastUpdate: 4/30/2025, 5:22:00 PM