webpack
webpack
webpack是一个用于现代JavaScript应用程序的静态模块打包工具,当webpack处理应用程序时,会在内部构建一个依赖图,此依赖图对应到项目所需的每个模块
从v4.0.0开始,webpack可以不再引入一个配置文件来打包项目,但仍然有着高度可配置性
功能:
-
模块打包:将项目中的各个模块及依赖打包成一个或多个文件,优化代码结构和加载速度。
-
按需加载:通过代码拆分,将不同模块分成小包,只在需要时加载,减少初次加载时间。
-
资源管理:通过各种Loaders和Plugins处理样式、图片、字体等各种静态资源,支持自动优化。
-
开发友好:提供热更新、实时重载等功能
核心:
-
入口entry:Webpack 构建的起点,通常指向应用的主文件
-
输出output:Webpack 打包后的文件存放位置与文件名配置
-
加载器loaders:用于处理非JavaScript 文件(如 CSS、图片)的工具,通过加载器可以将这些资源转为可打包模块
-
插件plugins:Webpack 功能扩展工具,用于优化、注入变量等
概念:Webpack 从入口文件开始,收集所有的 module,将其按照依赖关系和分割规则组织成 chunk,对 chunk 进行优化和压缩,最终生成 bundle 文件
-
module模块:是整个应用的基本组成部分,一切在项目中被引入的文件对于webpack来说都是一个模块
-
chunk代码块:打包过程中,一组module 的集合,根据文件间的依赖关系主要有三种类型
-
entry chunk:包含 webpack runtime 和模块依赖关系
-
async chunk:异步加载的模块
-
runtime chunk:运行时代码
-
-
bundle打包结果:是最终输出的一个或多个打包好的文件,是chunk经过压缩、合并等处理后的产物。这些文件会在览器中被直接加载。
打包构建流程
-
初始化:
-
从配置文件和 Shell 语句中读取合并参数
-
实例化 Compiler 对象(全局单例,负责把控整个打包构建流程,包含完整的webpack配置信息,全局只有一个),加载所有配置的插件
-
执行 compiler.run() 开始编译
-
-
编译阶段:
-
根据配置中的 entry 找出所有入口文件
-
调用 Loader 对模块进行转换
-
通过 AST 分析模块依赖,递归本步骤直到所有入口依赖的文件都经过处理
-
使用 Module 和 Dependency 管理代码模块之间的关系
-
-
输出阶段:
-
把模块组合成 Chunk
-
把 Chunk 转换成文件,输出到文件系统中
-
根据配置确定输出的路径和文件名
-
安装
npm install --save-dev webpack //安装最新版
npm install --save-dev webpack@<version> //安装指定版本
npm install --global webpack@4.0.0 //全局安装
npm install --save-dev webpack-cli@3.3.12 //安装4+以上版本还需要另外安装CLI
npm i webpack@4.0.0 webpack-cli@3.3.12 --save-dev
安装完毕后会生成:package-lock.json
,把webpack所有的依赖包(依赖关系)全部扁平化出来
打包
不使用配置文件:
npx webpack ./src/index.js -o ./dist // 把scr/index.js打包到dist文件夹中
-
entry file:入口文件的路径,本文中是./src/index.js的路径
-
destination for bundled files :打包后存放的路径
使用配置文件:
npx webpack --config webpack.config.js
在根目录创建webpack.config.js
文件
const path = require('path'); //path是node中自带的包,表示处理当前文件的目录
module.exports = {
mode: 'development',
// 入口文件配置
entry: {
main: './src/main.js',
// index:'./src/index.js' //多入口
},
// 出口文件配置
output: {
path: path.resolve(__dirname, 'dist'), //把dist目录解析为绝对路径;__dirname表示全局变量
filename: 'index.js', //单入口
// filename: '[name].js', //多入口
},
// 模块:例如解读CSS,图片如何转换,压缩
module: {},
// 插件:用于生产模板和各项功能
plugins: [],
// 配置webpack开发服务功能
devServer: {}
};
-
path:Node.js Path模块
-
path.resolve:将文件转化为绝对路径
-
__dirname:Node.js中的全局变量,指向当前文件的所在目录
入口entry是查找依赖关系的起点(内部依赖图的开始),进入入口起点后,webpack会找出哪些模块和库是入口起点的依赖
loader模块
模块主要运用各种loader用于对模块的源代码进行转换,将sass、less或ES6/TS编译为HTML可以识别的CSS、JS等,或者进行一些代码、图片的压缩等处理
通过配置module.rules
,来处理某些类型的文件
loader中的执行顺序是从右到左,支持链式调用 ,如:use:['style-loader','css-loader']
先执行css-loader再执行style-loader
noParse
:过滤不需要解析的文件
rules
:设置解析规则数组
-
{test:xxx}
:匹配特定条件,一般提供一个正则表达式或正则表达式数组 -
{include:xxx}
:匹配特定条件,一般提供一个字符串式或字符串数组 -
{exclude:xxx}
:排除特定条件,一般提供一个字符串式或字符串数组 -
{loader:[xxx] || [xxx]}
:解析需要的loader,一般提供一个字符串式或字符串数组
常用loader
-
style-loader
:将CSS 内容作为内联样式嵌入到 HTML 文件标签中的<style>
(将解析后的CSS内容添加到页面的DOM中)修改 CSS 后会自动更新页面样式而不刷新整个页面(热更新时)——开发环境
使用
MiniCssExtractPlugin
提取CSS到单独文件中,优化加载性能、减少JS文件体积——生产环境 -
css-loader
:解析CSS文件将CSS内容转化为JS模块 ,如果有@import
和url()
引入的CSS文件会继续递归加载处理 -
sass-loader
:负责将Sass文件转换为CSS -
less-loader
:负责将less文件转换为CSS -
vue-loader
:用于处理vue单文件组件 -
html-loader
:用于导入HTML文件,并将其中的资源作为依赖处理 -
ts-loader
:负责将TS文件转换为JS -
babel-loader
:将ES6+的JS代码转化为浏览器兼容的ES5代码 -
file-loader
:将图像文件复制到输出目录,并返回其URL(大图)避免生成过大的JS文件 -
url-loader
:类似于 file-loader,但支持将小图像转换为Base64 格式,嵌入到JavaScript 中。可以通过配置设置大小阈值,超过该值的图像将使用 file-loader 处理 -
responsive-loader
:根据不同的视口大小生成多种尺寸的图像,适合响应式设计。支持自动生成适合不同屏幕的图片格式
Plugin插件
项目中的JS文件通过配置的插件进行解析
插件是用来拓展webpack功能,会在构架过程中生效,执行相关任务
区别:
-
loader是在打包构建过程中处理源文件的(JSX、SCSS、LESS)一次处理一个 在module.rules中配置
-
插件并不直接操作单个文件,直接对整个构建过程起作用 在plugins数组中配置
plugin安装完毕后需要进行引入和配置
例:
-
安装 :
npm i html-webpack-plugin
-
引入:
const HtmlWebpackPlugin=require('html-webpack-plugin')
-
配置:
plugins: [ new webpack.ProgressPlugin(), new HtmlWebpackPlugin({ template: './src/index.html' }), ],
常用插件
-
HtmlWebpackPlugin
:自动生成一个HTML文件,并自动将打包好的JS/CSS文件插入到这个HTML中支持配置一个 minify 对象,用来配置压缩HTML
module.export = { plugins: [new HtmlWebpackPlugin({// 动态⽣成 html ⽂件 template: "./index.html", minify: {// 压缩HTML removeComments: true, // 移除HTML中的注释 collapseWhitespace: true, // 删除空⽩符与换⾏符 minifyCSS: true // 压缩内联css }, }) ] }
-
CleanWebpackPlugin
:每次构建前会删除dist目录下的旧文件,确保输出文件夹干净 -
MiniCssExtractPlugin
:将CSS从JS文件中提取到单独的文件中,从而减少CSS文件的内联,优化浏览器加载性能默认webpack会把CSS内嵌到JS中,MiniCssExtractPlugin可以将CSS从JS中提取出生成一个独立的css文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); module.exports = { module: { rules: [ { test: /\.css$/, use: [ // 提取成单独的⽂件 MiniCssExtractPlugin.loader, 'css-loader', ], exclude: /node_modules/, }, ], }, plugins: [ new MiniCssExtractPlugin({ // 定义输出⽂件名和⽬录 filename: 'asset/css/main.css', }), ], optimization: { minimize: true, minimizer: [ // 压缩 cssnew CssMinimizerPlugin({}), ], }, };
-
OptimizeCSSAssetsPlugin
:压缩CSS代码 -
ImageMinimizerPlugin
:减小图片文件大小 -
TerserWebpackPlugin
: 压缩JS代码,是webpack4以及上版本中默认的压缩插件const TerserPlugin=require("terser-webpack-plugin") optimization:{ minimize: true, minimizer:[new TerserPlugin({}),// 显示配置 ], }
-
CopyWebpackPlugin
:可以将静态资源从一个目录复制到另一个目录 -
ProvidePlugin
:自动加载模块,不需要在每个文件中显式引入,可以在整个应用中提供全局变量 -
DefinePlugin
:用于创建全局常量,通常用于设置不同环境下的配置(开发环境/生产环境) -
EnvironmentPlugin
:将process.env中的环境变量注入到代码中 -
SplitChunksPlugin
:将共享的库和模块提取到独立的文件中,减少页面初次加载时的体积可以通过cacheGroups来分离第三方库和应用中的公共模块
-
vendor:将 node_modules 中的模块提取到一个独立的文件
-
common:将多个模块中共享的代码提取到一个文件
optimization:{ splitChunks:{ chunks:'all', cacheGroups:{ vendor:{ test:/[\\/]node_modules[\\/]/, name:'vendor', chunks:'all', priority:10, //确保vendor优先提取 }, common:{ name:'common', //公共代码的文件名 minSize:3000, //最小文件大小 minChunks:2, //至少被2个模块共享的代码才会被提取 } } } }
-
-
HotModuleReplacementPlugin
: 开发时可以通过热模块替换(HMR)避免刷新整个页面 -
BundleAnalyzerPlugin
:分析构建产物的大小,有助于优化打包结果plugins:[ new webpack.DefinePlugin({ 'process.env.NODE_ENV':JSON.stringify('production') }) ]
HMR与Live-Reload
-
Live-Reload:开发服务器会监控文件变化,当文件变化时,会通知浏览器重新加载整个页面
原理:主要通过监控文件系统上的变化,当文件发生变化时,触发浏览器的刷新请求。
它一般依赖于开发服务器,开发服务器会监听源文件的更改(例如,通过webpack-dev-server
或其他工具)。当文件被修改时,服务器会通知浏览器进行刷新。优点:实现简单、开箱即用,支持所有类型的文件修改。
缺点:每次修改都会刷新整个页面,状态丢失,页面加载时间较长。
更适合简单应用或纯前端项目
devServer:{ contentBase: './dist', liveReload:true }
-
HMR:它只会替换更新的模块,而不需要重新加载整个页面
原理:HMR 通过 Webpack 或其他模块打包工具集成,基于模块系统,它会监控每个模块的变化。当某个模块发生变化时,只替换该模块,而不重新加载整个页面,通常需要通过 WebSocket 或服务器推送的方式来实现。
优点:页面不会完全刷新,能够保留应用状态。对于大型应用来说,HMR 能显著提高开发效率。
缺点:实现相对复杂,对于一些变化(例如:改变了模块之间的依赖关系,或大规模改动)可能无法完美替换,依然需要刷新。
更适合大型应用、复杂前端框架
devServer:{ hot:true, }, plugins:[ new webpack.HotModuleReplacementPlugin() ]
提升热跟新效率
-
减少不必要的文件监听:可以通过配置
watchOptions
的 ignore 来忽略一些变化频率高且不需要监听的文件,如node_moduleswatchOption:{ ignored: /node_modules/ }
-
优化模块解析:添加
resolve
中的alias和extensions,减少Webpack 在解析模块时的搜索范围,可以显著加快打包速度resolve:{ alias:{ '@components': path.resolve(__dirname,'src/components') }, extensions:['.js','.jsx','.json','.css'] }
-
使用热更新插件:使用如
webpack-dev-server
或webpack-hot-middleware
等工具,搭配react-hot-loader
或vueloader
等插件,可以实现更高效的热更新 -
开启缓存:利用 Webpack 的cache功能,避免每次重新构建都需要从头开始,极大提高构建速度
webpack4+版本内置了一些缓存机制,开启缓存:
cache:{ type:'filesystem' }
-
分离开发和生产配置: 在开发环境中尽量少用或不用代码压缩、文件优化等耗时操作,保留这些优化仅在生产环境中执行
使用
webpack-merge
将开发和生产环境的配置文件分开const { merge } = require('webpack-merge'); const commonConfig = require('./webpack.common'); const devConfig = merge(commonConfig, { mode: 'development', devtool: 'inline-source-map' }); const prodConfig = merge(commonConfig, { mode: 'production', optimization: { minimize: true } }); module.exports = process.env.NODE_ENV === 'production' ? prodConfig : devConfig;
插件原理
通过使用 Tapable 库实现事件触发机制,插件可以在 Webpack 构建生命周期的不同阶段挂钩,执行特定的任务。
-
Webpack 使用 Tapable 来创建钩子(hooks),这些钩子允许插件在构建过程的特定时机插入逻辑
-
同步钩子SyncHook:按顺序执行
-
异步钩子:
-
AsyncSeriesHook:异步钩子,按顺序执行
-
AsyncParallelHook:异步钩子,并行异步执行
-
-
-
插件通过定义一个包含 apply 方法的类来实现。apply 方法接收一个 compiler 对象(包含了所有的构建配置和状态)
-
插件在 apply 方法中使用 compiler 对象上的钩子,注册回调函数,在构建过程中执行自定义逻辑
class MyPlugin {
apply(compiler) {
compiler.hooks.emit.tapAsync('MyPlugin', (compilation, callback) => {
// 在 emit 阶段执行特定逻辑
console.log('This is my custom plugin!');
callback();
});
}
}
module.exports = MyPlugin;
开发配置
const path=require('path')
module.exports = {
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress:true, //gzip压缩
port: 3000,
open: true,
https:true
host:'0.0.0.0', //ip地址
proxy:{
'api':{
target:'http://localhost:3000', //代理地址
changeOrigin:true
pathRewrite:{'^/api':''}
}
},
watchOption:{ //进行文件监控
poll:1000, //每秒检查一次变更
igonred: /node_modules/
},
clientLogLevel:'info' //输出日志,其他可选值:warning、error
}
};
启动webpack dev server:npx webpack serve
运行机制
服务端流程:
-
启动 webpack-dev-server,建立 websocket 连接
-
监听文件变化,触发重新编译
-
编译完成后,生成新的 hash 值
output:{ path:path.resolve(__dirname,'dist') filename:'[name].[hash].js' //整个项目构建相关 filename:'[name].[chunkhash].js' //和打包的chunk有关,不同的入口生成不同的chunkhash filename:'[namel.[contenthash].js //根据文件内容来定义hash }
-
推送更新消息到客户端
客户端流程:
-
接收服务端消息
-
对比新旧模块 hash
-
通过 JSONP 请求最新模块代码
-
执行模块更新回调
性能优化
减少搜索范围
-
使用 resolve.modules 指定模块搜索目录
-
配置 resolve.alias 创建路径别名
-
合理使用 resolve.extensions 配置
代码分割
通过动态导入或入口配置,将应用拆分成多个小块,按需加载,提高首次加载速度。
module.exports = {//...
optimization: {
splitChunks: {
chunks: 'all', // 值有 all拆分所有类型模块;async拆分import()动态导入的模块; initial拆分入口文件中引入的模块
minSize: 20000, // ⽣成 chunk 的最⼩体积(以 bytes 为单位)。
minRemainingSize: 0,
minChunks: 1, // 拆分前必须共享模块的最⼩ chunks 数。
maxAsyncRequests: 30, // 按需加载时的最⼤并⾏请求数。
maxInitialRequests: 30, // ⼊⼝点的最⼤并⾏请求数。
enforceSizeThreshold: 50000,
cacheGroups: {
defaultVendors: {
test: /[\/]node_modules[\/]/, //第三⽅模块拆出来
priority: -10,
reuseExistingChunk: true,
},
util.vendors: {
test: /[\/]utils[\/]/, //公共模块拆出来
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
};
资源压缩
使用 TerserWebpackPlugin
对 JavaScript 进行压缩(生产模式自动开启),使用 css-minimizer-webpack-plugin
压缩 CSS,减少文件体积。
图片优化
使用 image-webpack-loader
压缩图片,降低加载时间,改善用户体验。
module.exports = {module: {
rules: [
{
test: /\.(png|jpg|gif|jpeg|webp|svg)$/,
use: ["file-loader",
{
loader: "image-webpack-loader",
options: {
mozjpeg: {
progressive: true,
},
optipng: {
enabled: false,
},
pngquant: {
quality: [0.65, 0.9],
speed: 4,
},
gifsicle: {
interlaced: false,
},
},
},
],
exclude: /node_modules/, //排除 node_modules ⽬录
},
]
},
}
其他图片处理加载器:
-
file-loader
:将图像文件复制到输出目录,并返回其URL(大图)避免生成过大的JS文件 -
url-loader
:类似于 file-loader,但支持将小图像转换为Base64 格式,嵌入到JavaScript 中。可以通过配置设置大小阈值,超过该值的图像将使用 file-loader 处理 -
responsive-loader
:根据不同的视口大小生成多种尺寸的图像,适合响应式设计。支持自动生成适合不同屏幕的图片格式
预加载和预取
使用 Webpack 的webpackPrefetch
和webpackPreload
提高资源加载效率。
缓存管理
设置合适的缓存策略,通过 hash 文件名管理缓存,避免用户下载过期资源。
output:{
filename: '[name].[contenthash].js',
chunkFilename:'[name].[contenthash].js'
}
打包缓存优化
使用 cache-loader
或 hard-source-webpack-plugin
对打包结果进行缓存
优化依赖
定期检查,使用npm prune
删除不必要的依赖
Tree Shaking
通过 ES6 模块(import/export)的静态分析,去除未使用的代码,减小打包后的体积 (import必须放在模块顶层)
CommonJS:动态导入,运行时加载,不支持tree-shaking,可以放在代码任何位置,导入用require,导出用module.exports/exports(导入导出都是值的拷贝)
依赖于ES6模块的静态结构特性,能够在编译时确定模块的依赖关系,以此识别出哪些代码未被使用
module.exports={
mode:'production', //生产模式自动启用
optimization:{
usedExports:true //启用Tree shaking
}
}
-
生产模式下默认开启
-
仅适用于ES6模块
-
只能处理模块级别,不能处理函数级别的冗余
-
只能处理JS相关冗余代码,不能处理CSS冗余代码(可以通过
purgecss-webpack-plugin
对CSS进行tree shaking)const path = require("path"); const PurgecssPlugin = require("purgecss-webpack-plugin"); const glob = require("glob"); // ⽂件匹配模式module.exports = {//... plugins: [ ...new PurgeCSSPlugin({ paths: glob.sync(`${PATH.src}/**/*`, { nodir: true }), }) ], };
按需加载
不需要一次性加载所有的 JS 文件,而应该在不同阶段去加载所需要的代码。
懒加载路由可以打包到一个 chunk 里面。比如某个列表页和编辑页它们之间存在相互跳转,如果对它们拆分成两个import()
JS资源加载模块,在跳转过程中视图会出现白屏切换过程。
在跳转期间,浏览器会动态创建 script 标签来加载这个 chunk 文件,在这期间,页面是没有任何内容的。所以一般会把路由懒加载打包到一个chunk 里面
注意:动态导入 import() 一个模块,这个模块就不能再出现被其他模块使用同步import 方式导入,否则会造成动态import()失效
gzip
实现对资源进一步压缩
-
当用戶访问 web 站点的时候,会在请求头中设置
accept-encoding:gzip
,表明浏览器是否支持 Gzip -
服务器在收到请求后,判断如果需要返回 Gzip 压缩后的文件,那么服务器就会先将JS\CSS 等其他资源文件进行Gzip 压缩后再传输到客戶端,同时将响应头设置
content-encoding:gzip
一般情况下我们并不会让服务器实时 Gzip 压缩,而是利用 webpack 提前将静态资源进行 Gzip 压缩,然后将 Gzip 资源放到服务器(
compression-webpack-plugin
实现) -
反之,则返回源文件。
-
浏览器在接收到服务器返回的文件后,判断服务端返回的内容是否为压缩过的内容,是的话则进行 解压操作。,当请求需要的时候直接将 Gzip 资源发送给客戶端
作用域提升
Scope Hoisting
可以让 webpack 打包出来的代码文件体积更小,运行更快
在开启 Scope Hoisting 后,构建后的代码会按照引入顺序放到一个函数作用域里,通过适当重命名某些变量以防止变量名冲突,从而减少函数声明和内存花销
需要注意: Scope Hoisting 需要分析模块之间的依赖关系,所以源码必须采用 ES6 模块化语法
只需要在 plugins 里面使用即可,或者直接开启生产环境也可以让作用域提升生效
module.exports = {
//⽅式1
mode: 'production',
//⽅式2
plugins: [// 开启 Scope Hoisting 功能new
webpack.optimize.ModuleConcatenationPlugin()
]
}
构建工具区别
vite
:
-
基于原生 ES 模块,使用浏览器支持的 import,在旧浏览器中需要polyfill(一种用于在旧版浏览器或环境中实现现代 Web 特性的代码。它通过模拟新 API 的行为,为旧浏览器提供功能上的替代实现,从而解决浏览器兼容性问题)
-
开发时不打包,使用按需加载
-
适合现代浏览器,支持 HMR
-
开发启动快,热更新速度快,构建速度快,使用rollup打包,支持ES6+特性
webpack
:
-
功能强大:能处理各种文件类型,如支持 JavaScript、Css、SASS、LESS图片等多种资源的打包。
-
模块化和插件生态:拥有广泛的plugin和loaders 生态,几乎能满足所有前端构建需求,且支持各种plugin和loaders来处理不同类型的文件。
-
灵活性:配置非常灵活,可以满足各种复杂的需求,如代码分割、懒加载、热更新、树摇等
-
适用于复杂的应用程序、企业级项目,但是配置相对复杂,在大规模应用中构建速度可能较慢
-
初次构建较慢,增量构建较快
rollup
:
-
专注于 ES 模块:Rollup 主要是为现代 JavaScript(尤其是ES6 模块)打包而设计的,特别适合用于构建库和组件。
-
优化效果好:在打包时可以更好的做Tree Shaking(树摇),可以移除未使用的代码,从而生成更小的打包文件。
-
输出格式多样:支持多种输出格式,如CommonJS、ESM、IIFE、UMD 等。
-
适用于构建JS库,对模块化支持友好
-
在构建速度上比webpack快
-
采用增量构建技术,只对修改过的模块进行重新打包
-
Rollup对 ES6 模块进行了深度优化,能够更高效地进行静态分析和代码分割。它通过静态分析确定哪些代码是真正需要的,从而更有效地移除未使用的代码;Webpack:由于其处理的模块类型更多,且需要处理复杂的依赖关系,在代码分析和优化上可能不如 Rollup 高效
-
-
相比于webpack,插件生态较弱,配置的灵活性较差,不适合大型单页应用
Parcel
:
-
零配置:开箱即用,无需任何配置,可以自动检测项目中的依赖,自动进行打包
-
构建速度快:利用多核 CPU 进行并行构建,特别适用于快速开发和原型设计
-
内置支持类型:Parcel内置支持TypeScript、JSX、SASS 等常见的前端技术,无需额外安装loader或插件
-
适合小型项目或快速原型开发
-
相比Webpack,插件和社区支持较少,缺乏精细化控制,不能满足某些复杂的定制需求。
Grunt
:
-
基于任务的构建工具,通过配置任务来完成文件的压缩、合并、转译等工作,更偏向于通过配置来进行任务管理
-
配置简单,任务定义明确,适合简单的自动化操作
-
插件丰富,几乎所有常见的构建任务都可以找到现成的插件。易于集成到现有项目中,尤其是较小的项目
-
但性能较差,任务是顺序执行的,不能并行执行任务,效率较低。任务定义过于单一,不支持现代前端开发中的模块化和组件化需求。相较于 Gulp 和 Webpack,灵活性较差。
Gulp
:
-
流式构建工具,依赖于 Node.js 流的特性,通过管道(pipe)将任务链接起来,通过 JavaScript 代码来描述任务,相比于 Grunt 的配置文件,Gup 提供了更大的灵活性。
-
支持任务并行执行,性能更好
-
使用流处理数据,避免了大量的临时文件,提高了构建效率
-
配置灵活,但相对复杂,支持通过 JavaScript 代码来编写任务,灵活性较高。
webpack生命周期
webpack使用Tapable库来管理这些生命周期的钩子,插件可以通过这些钩子在特定阶段执行自定义逻辑
-
初始化阶段:解析配置文件,初始化各种插件和加载器,并创建 Compiler 对象
-
编译阶段:从入口开始,递归解析和构建依赖关系图 (每个模块都会被解析为一个节点,模块之间的依赖关系作为边)
-
构建模块阶段:处理每个模块的依赖,并将其转化为内部表示;包括对模块的解析、加载和转换
-
优化阶段:webpack进行各种优化操作,如代码分割、Tree Shaking等
-
生成阶段:根据优化后的模块生成最终的输出文件,包括对代码的压缩和其他生成操作
-
输出阶段:将生成的文件写入指定的输出目录中
beforeRun
和run
:在编译开始之前compile
和compilation
:在编译创建时emit
:在生成输出文件之前done
:在编译完成时