webpack
webpack是什么?
Webpack是一个模块打包工具,核心功能是将各种资源文件如 JavaScript、CSS、图片、字体等文件视为模块(Module),通过代码中的 import
/require
语句建立依赖关系,形成依赖图,根据这个图,将每个模块组合打包成静态资源,配置的 Loader(处理模块内容)和 Plugin(介入打包生命周期)决定了你的打包策略,最终,这些**静态资源(bundles)**通常只会是 JavaScript、CSS、HTML、图片,其他组合不了的文件,会原样输出。
加载器(Loader)的作用
将非 JS 资源(如 CSS、图片)需通过对应的 Loader 转换为 Webpack 可处理的模块
插件(Plugin)的作用
介入打包生命周期中,执行更复杂的任务,例如:
- 资源优化:
TerserPlugin
压缩 JS 代码。 - 环境注入:
DefinePlugin
定义全局常量。 - 文件生成:
HtmlWebpackPlugin
自动生成 HTML 入口。
依赖关系图是怎么生成的?
通过代码中的 import
/require
语句建立依赖关系
webpack的运行流程的概念术语
当 webpack 处理应用程序时,它会在内部从一个或多个入口点构建一个 依赖图(dependency graph),然后将你项目中所需的每一个模块组合成一个或多个 bundles,它们均为静态资源,供浏览器加载。
-
Bundle:打包后的最终生成文件,一个或多个,通常后缀名是 .bundle.js 或 .bundle.css 等,是可以在浏览器中运行的文件
-
Module(模块):webpack将所有的代码都视为模块。可以是JS文件、CSS文件、图片等。
-
Chunk(代码块):Chunk是多个Module的集合,用于代码的分割和按需加载。可以是按页面划分的代码块、按路由划分的代码块、公共代码块等等。不同的代码分割策略生成不同的chunk
总的来说,
- Module就是我们打包目录下编写的项目文件;
- Chunk是将多个Module组合在一起形成的代码块,是打包过程中的一个中间产物;
- Bundle是Chunk合并后得到的资源文件;
chunk分类
-
initial chunk 也就是entry chunk,入口文件
-
async chunk 也就是通过动态加载的entry chunk,动态文件
-
split chunk 也就是提取出来的chunk,分出来的入口、输出、loader、plugin、模块、依赖图等
webpack的配置
五大核心属性
-
entry(入口):指定webpack 从哪个文件开始打包,支持配置多个
-
多entry应用场景:需要SEO的多页面应用
-
需要插件HtmlWebpackPlugin、CommonsChunkPlugin分包
-
-
output(输出):指定 Webpack 打包完的文件输出路径,命名等
-
filename:输出bundle的文件名
-
chunkfilename:输出chunk的文件名
-
path:文件输出目录,必须是绝对路径
-
publicPath:指定在浏览器中访问打包后的资源时的公共路径
-
-
loader(加载器):将非 JavaScript 文件转换为 JavaScript 模块的插件
-
由于非 JavaScript 文件类型多样,也对应有多种loader
-
模块化有多种方式实现,包括 CommonJS、AMD、ES6 等规范
-
作用:
-
处理文件依赖
-
转换文件格式:sass-loader
-
代码转换:如babel-loader
-
静态资源处理:如file-loader、url-loader
-
-
-
plugins(插件):扩展 Webpack 的功能
-
mode(模式):webpack4新增的配置项
- 三种模式:开发模式:development,生产模式:production,无模式:none
-
设置为 'development'时,process.env.NODE_ENV被设为'development',启用调试工具,如热更新,devtool
-
设置为 'production'时,process.env.NODE_ENV被设为'production',启用优化插件,如代码压缩,Tree Shaking
-
设置为 'none'时,不设置process.env.NODE_ENV
-
- 三种模式:开发模式:development,生产模式:production,无模式:none
其他属性
-
resolve(解析)
-
alias
-
extensions
-
-
optimization(优化)
-
minimize 是否需要压缩 bundle,默认 true
-
minimizer 配置压缩工具,如 TerserPlugin
-
runtimeChunk 是否需要将所有生成 chunk 之间共享的运行时文件
-
noEmitOnErrors
-
splitChunks (代码分割)
-
chunks
-
async: 对异步(async)引入的包进行分包操作(默认值)
-
inital:对同步引入的包进行分包操作
-
all:all 对所有引入的包全部进行分包操作
-
-
minChunks
-
maxSize //307200,
-
maxAsyncRequests // 30,
-
maxInitialRequests //30
-
cacheGroups
-
-
-
cache (持续化缓存:Webpack5 新特性)
-
type: memory/filesystem
-
cacheDirectory
-
maxAge:缓存失效时间
-
-
devtool (调试工具):此选项控制是否生成,以及如何生成 source map
-
属性值:
-
false:关闭调试
-
true:开启调试
-
eval-cheap-module-source-map 开发环境最优最轻量
-
-
devtool类型:devtool官方文档
-
-
externals (打包不包括的模块)
-
devServer(开发服务器)
-
contentBase (静态资源路径)
-
compress (启用 gzip 压缩)
-
host (ip:localhost 或本机ip)
-
port (端口)
-
proxy(访问代理)
-
https (启用https)
-
webpack.config.js 示例
通常会分为webpack.base.config.js、webpack.dev.config.js、webpack.prod.config.js
但react 脚手架用config.overide.js,支持扩展webpack配置
// 尝试使用环境变量,否则使用根路径
const ASSET_PATH = process.env.ASSET_PATH || '/';
module.exports = {
entry: {//入口
//对象形式
app: './src/main.ts'
},
output: {//出口
// path: 文件输出目录,必须是绝对路径
// path.resolve()方法返回一个绝对路径,__dirname 当前文件的文件夹绝对路径
path: path.resolve(__dirname, "dist"),
// filename: 输出文件名
filename: "[name].bundle.js",
chunkfilename:"[name].chunk.js",
//publicPath:指定在浏览器中访问打包后的资源时的公共路径
publicPath: ASSET_PATH
},
resolve: {
extensions: ['.js', '.vue', '.json', '.ts', '.tsx'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src')
}
},
module: {
rules: [
{
test: /\.ts(x)?$/,
loader: 'ts-loader',
exclude: /node_modules/,
options: {
appendTsSuffixTo: [/\.vue$/],
transpileOnly: true,
}
},
...
]
},
optimization: { // 举例
splitChunks: {
chunks: 'all',
cacheGroups: {
// 第三方组件
libs: {
////指定chunks名称
name: 'chunk-libs',
//符合组的要求就给构建venders
test: /[\\/]node_modules[\\/]/,
//priority:优先级:数字越大优先级越高,因为默认值为0,所以自定义的一般是负数形式,决定cacheGroups中相同条件下每个组执行的优先顺序。
priority: 10,
// 仅限于最初依赖的第三方
chunks: 'initial'
},
elementUI: {
// 将elementUI拆分为单个包
name: 'chunk-elementUI',
// 重量需要大于libs和app,否则将打包到libs或app中
priority: 20,
// 为了适应cnpm
test: /[\\/]node_modules[\\/]_?element-ui(.*)/
},
//公共组件
commons: {
name: 'chunk-commons',
// can customize your rules
test: resolve('src/components'),
minChunks: 3,
priority: 30,
//这个的作用是当前的chunk如果包含了从main里面分离出来的模块,则重用这个模块,这样的问题是会影响chunk的名称。
reuseExistingChunk: true,
//最大初始化加载次数,一个入口文件可以并行加载的最大文件数量,默认3
maxInitialRequests: 3,
//表示在分离前的最小模块大小,默认为0,最小为30000
minSize: 0
},
echarts: { // split echarts libs
name: 'chunk-echarts',
test: /[\\/]node_modules[\\/]_?echarts(.*)/,
priority: 40,
chunks: 'async',
reuseExistingChunk: true
},
monaco: { // split monaco libs
name: 'chunk-monaco',
test: /[\\/]node_modules[\\/]_?monaco(.*)/,
priority: 40,
chunks: 'async',
reuseExistingChunk: true
},
'project-components': { // split project libs
name: 'chunk-project-components',
test: resolve('src/views/project'),
priority: 50,
chunks: 'async',
reuseExistingChunk: true
},
'teachers-components': { // split teacher libs
name: 'chunk-teachers-components',
test: resolve('src/views/teachers'),
priority: 60,
chunks: 'async',
reuseExistingChunk: true
},
'utils': { // split utils libs
name: 'chunk-utils',
test: resolve('src/utils'),
priority: 70,
chunks: 'async',
reuseExistingChunk: true
},
}
}
},
devServer: {
contentBase: path.resolve(__dirname, "dist"), //静态资源路径
open: true, //用于设置 server 启动后是否自动打开浏览器
inline: true, //代码保存自动刷新页面
// hot: true, //进行热更新(局部刷新,不刷新整个页面)
// openPage: '/different/page', //指定deserver 编译完成后自动打开的页面
// https: true, //用于设置是否启用https
// compress: true, //对devServer 所有服务启用 gzip 压缩
// headers: {'X-Custom-Foo': 'bar'}, //在所有响应中添加首部内容
host: '0.0.0.0', //用于指定devDerve使用的host,如果你希望服务器外部可以访问,设定如 host: '0.0.0.0'
// port: 8080,
proxy: {
'/monitor': {
secure: false, //默认情况下,不接受运行在 HTTPS 上,且使用了无效证书的后端服务器。如果你想要接受,只要设置 secure: false 就行
changeOrigin: true, //changeOrigin参数设置为true, 本地就会虚拟一个服务器接收你的请求并代你发送该请求
target: 'http://192.168.2.8:8012/monitor',
pathRewrite: { //重写路径,不需要代理到api的接口
'^/monitor': ''
}
},
}
},
}
webpack 的工作原理
运行流程
https://juejin.cn/post/7043667925244330020
Webpack 的运行流程可以更细化为以下几个步骤:
- 解析配置文件:Webpack 会读取项目根目录下的配置文件,如 webpack.config.js,解析出入口文件、输出文件、模块解析规则等配置信息。
- 解析入口文件:Webpack 会根据配置文件中的入口文件路径,找到入口文件,并从入口文件开始递归解析所有依赖的模块。Webpack 会根据模块解析规则,如 resolve.extensions、resolve.alias 等,来确定模块的路径和文件名。
- 解析模块:Webpack 会根据模块解析规则,如 resolve.modules、resolveLoader.modules 等,来查找模块所在的目录。Webpack 会根据模块的类型,如 JavaScript、CSS、图片等,使用相应的 loader 进行转换。Webpack 会根据模块之间的依赖关系,使用模块打包器(如 webpack.optimize.CommonsChunkPlugin)将模块合并成一个或多个输出文件。
- 打包输出:Webpack 会将所有解析后的模块打包成一个或多个输出文件,输出文件的路径和文件名由配置文件中的输出路径和文件名规则决定。Webpack 会根据模块之间的依赖关系,使用模块打包器(如 webpack.optimize.CommonsChunkPlugin)将模块合并成一个或多个输出文件。
- 优化打包结果:Webpack 会对打包结果进行优化,包括代码压缩、去重、分离等操作,以减小输出文件的体积和提高加载速度。Webpack 会根据配置文件中的 optimization 选项,如 minimize、splitChunks 等,来进行优化。
- 输出打包结果:Webpack 会将优化后的打包结果输出到指定的输出路径中,供浏览器或服务器加载和执行。Webpack 会根据配置文件中的 output 选项,如 path、filename 等,来确定输出文件的路径和文件名。
需要注意的是,Webpack 的运行流程是高度可配置的,可以通过配置文件和插件来定制各个步骤的行为和结果。同时,Webpack 的运行流程也是高度复杂的,需要深入理解其原理和机制才能更好地使用和调试。
Webpack 的构建过程
三个阶段:初始化、构建、生成
初始化,主要是根据配置信息设置内置的各类插件
Make - 构建阶段,从 entry 模块开始,执行:
-
读入文件内容
-
调用 Loader 转译文件内容
-
调用 acorn 生成 AST 结构
-
分析 AST,确定模块依赖列表
-
遍历模块依赖列表,对每一个依赖模块重新执行上述流程,直到生成完整的模块依赖图 —— ModuleGraph 对象
Seal - 生成阶段,过程:
-
代码转译,如 import 转换为 require 调用
-
分析运行时依赖
-
遍历模块依赖图,对每一个模块执行:
-
合并模块代码与运行时代码,生成 chunk
-
执行产物优化操作,如 Tree-shaking
-
将最终结果写出到产物文件
参考资料:
官网概念文档:https://webpack.docschina.org/api/
https://juejin.cn/post/6991630925792542750
react/vue 的webpack配置:https://juejin.cn/post/7251501842986418237
评论区