01webpack基础知识
1 概述
1.1 什么是 webpack
1、 webpack
是一种前端资源构建工具,一个静态模块打包器(module bundler)。在webpack
看来, 前端的所有资源文件(js/json/css/img/less/...
)都会作为模块处理。它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle
)
2、 webpack
是一个前端模块化方案,更侧重模块打包,我们可以把开发中的所有资源(图片、js
文件、css
文件等)都看成模块,通过 loader
(加载器)和 plugins
(插件)对资源进行处理,打包成符合生产环境部署的前端资源
3、 webpack
是一个前端资源加载/打包工具。它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。
4、 webpack
更是明显强调模块化开发,而那些文件压缩合并、预处理等功能,不过是他附带的功能
1.2 webpack
的5个核心概念
1.2.1 入口(entry)
入口起点(entry point)指示 webpack
以哪个文件为入口起点开始打包,分析构建内部依赖图
默认值是 ./src/index.js
,但是可以通过配置配置文件进行配置,来指定一个或多个不同的入口起点。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的
例如:webpack.config.js
module.exports = {
entry: './src/index.js'
};
1 入口方式
单个入口
module.exports = {
entry: './path/to/my/entry/file.js',
};
数组多个入口
可以将一个文件路径数组传递给 entry
属性
module.exports = {
entry: ['./src/file_1.js', './src/file_2.js'],
output: {
filename: 'bundle.js',
},
};
对象语法
这是应用程序中定义入口的最可扩展的方式
“webpack 配置的可扩展” 是指,这些配置可以重复使用,并且可以与其他配置组合使用。这是一种流行的技术,用于将关注点从环境(environment)、构建目标(build target)、运行时(runtime)中分离。然后使用专门的工具(如 webpack-merge)将它们合并起来
module.exports = {
entry: {
app1: './src/app.js',
adminApp: './src/adminApp.js',
another:{
dependOn: 'app1', // 依赖于 app1 chunk
import: './src/app2.js',
}
},
};
描述入口的对象属性:
dependOn
: 当前入口所依赖的入口。它们必须在该入口被加载前被加载。filename
: 指定要输出的文件名称。import
: 启动时需加载的模块。library
: 指定 library 选项,为当前 entry 构建一个 library。runtime
: 运行时 chunk 的名字。如果设置了,就会创建一个新的运行时 chunk。在 webpack 5.43.0 之后可将其设为false
以避免一个新的运行时 chunk。publicPath
: 当该入口的输出文件在浏览器中被引用时,为它们指定一个公共 URL 地址
2 常见使用场景
分离 app(应用程序) 和 vendor(第三方库) 入口
module.exports = {
entry: {
main: './src/app.js',
vendor: './src/vendor.js',
},
output: {
// 配置输出
filename: '[name].[contenthash].bundle.js',
},
};
多页面应用程序
module.exports = {
entry: {
pageOne: './src/pageOne/index.js',
pageTwo: './src/pageTwo/index.js',
pageThree: './src/pageThree/index.js',
},
};
1.2.2 输出(output)
output 指示 webpack 打包后的资源 bundles 输出到哪里去,以及如何命名。主要输出文件的默认值是 ./dist/main.js
,其他生成文件默认放置在 ./dist
文件夹中。也可以通过在配置中指定一个 output
字段,来配置这些处理过程
例如:webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
// path:输出文件目录,路径必须是绝对路径, dist输出文件目录
path: path.resolve(__dirname, 'dist'),
// 文件名
filename: 'js/bundle.[contenthash].js',
// 引入所有公共资源的前缀
publicPath:'/',
// 给其他单独打包的文件 命名,非入口 js 文件
// 一般是 抽离出来的 其他模块 js 文件
chunkFilename:'[name]_chunk.js'
},
};
path
-
所有文件的输出文件的目标路径
-
必须是绝对路径
-
打包后项目文件在硬盘中的存储位置
-
HtmlWebpackPlugin生成的html文件,都会存放在以path为基础的目录下
publicPath
-
输出解析文件的目录,指定资源文件引用的目录 ,打包后浏览器访问服务时的 url 路径中通用的一部分
-
publicPath 并不会对生成文件的路径造成影响,主要是对项目页面里面引入的资源的路径做对应的补全,常见的就是css文件里面引入的图片
1.2.3 loader
webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中
在 webpack 的配置中,loader 有两个属性:
1、 test
属性,识别出哪些文件会被转换
2、 use
属性,定义出在进行转换时,应该使用哪个 loader
const path = require('path');
module.exports = {
output: {
filename: 'my-first-webpack.bundle.js',
},
module: {
rules: [
{
test: /\.txt$/,
use: 'raw-loader'
}
],
},
};
loader的主要作用
1、用于对模块的源代码进行转换
2、 loader 可以使你在 import 或"加载"模块时预处理文件
3、 loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL
4、 loader 允许你直接在 JavaScript 模块中 import CSS文件!
1.2.4 Plugins
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' })],
};
1.2.5 模式 mode
通过选择 development
, production
或 none
之中的一个,来设置 mode
参数,你可以启用 webpack 内置在相应环境下的优化。其默认值为 production
module.exports = {
mode: 'production'
}
选项 | 描述 | 特点 |
---|---|---|
development | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development 。启用 NamedChunksPlugin 和 NamedModulesPlugin |
能让代码本地调试运行的环境 |
production | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production 。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 TerserPlugin |
能让代码优化上线运行的环境 |
1.2.6 浏览器的兼容性
Webpack 支持所有符合 ES5 标准 的浏览器(不支持 IE8 及以下版本)。webpack 的 import()
和 require.ensure()
需要 Promise
1.2.7 环境
Webpack 5
运行于 Node.js v10.13.0+
的版
2 package.json
文件
package.json
文件主要是显示项目的名称、版本、作者、协议等信息
2.1 script 常用命令
dev": "webpack --mode development --config webpack.config.js --progress --colors"
// 指定 环境变量 production development
--mode development
// 可以传递 环境变量参数 到 webpack.config.js 中
--env development
// webpack.config.js 中接收
module.exports = env => {
console.log(env)
}
// 指定 要加载的 配置文件 --config 可以简写为 -c
--config
// 显示编译过程和百分比
--progress
// 编译结果显示不同的颜色
--colors
2.2 参数
字段name和version
在package.json中最重要的就是name和version字段
他们都是必须的,如果没有就无法install
name和version一起组成的标识在假设中是唯一的
改变包应该同时改变version
字段description
项目的描述,字符串类型
字段keywords
项目的关键字
字段homepage
项目官网的url
字段license
指定项目的许可证,可以使人知道使用的权利和限制的
字段scripts
“scripts”是一个由脚本命令组成的hash对象,他们在包不同的生命周期中被执行
3 webpack 优化配置
3.1 概述
webpack的性能优化,主要包含 开发环境性能优化和 生产环境性能优化。
开发环境性能优化包含
- 优化打包构建速度:HMR
- 优化代码调试:source-map
生产环境性能优化包含
- 优化打包构建速度:oneOf、babel缓存、多进程打包、externals、dll
- 优化代码运行的性能:缓存(contenthash)、tree shaking 、代码分割(code split)、懒加载/预加载 、PWA
3.2 HMR
模块热更新
样式文件:本身就可以使用HMR功能,因为style-loader内部实现了
js文件:默认不能使用HMR功能: 需要修改js代码,添加支持HMR功能的代码(只能处理非入口js文件的其他文件)
html文件: 默认不能使用HMR功能,同时会导致问题:html文件不能热更新了~ (其实也不用做HMR功能)
解决方案:修改entry入口,将html文件引入
3.2.1 webpack.config.js 配置
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// 001TODO: 将 要监控的html文件加入 入口中,可以实现 实时监控的功能
entry: ['./src/js/index.js', './src/index.html'],
output: {
filename: 'build.js',
path: resolve(__dirname, 'build'),
clean: true
},
module: {
rules: [
{
test: /\.s[a|c]ss$/i,
use: [
'style-loader',
'css-loader',
'sass-loader'
]
}
]
},
// 配置本地服务
devServer: {
static: resolve(__dirname, './dist'),
compress: true,
// 002TODO: 启用 HMR
hot: true,
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development'
}
3.2.2 非 入口 print.js
文件 监控
// 引入
import print from './print';
console.log('index.js文件被加载了~');
print();
function add(x, y) {
return x + y;
}
console.log(add(1, 3));
if (module.hot) {
// 一旦 module.hot 为true,说明开启了HMR功能。 --> 让HMR功能代码生效
module.hot.accept('./print.js', function() {
// 方法会监听 print.js 文件的变化,一旦发生变化,其他模块不会重新打包构建。
// 会执行后面的回调函数
print();
});
// 也可以不加回调函数
module.hot.accept('./print.js');
}
3.3 source-map
source-map:外联
可以追踪 错误代码准确信息 和 源代码的错误位置
inline-source-map:内联
可以追踪 错误代码准确信息 和 源代码的错误位置
只生成一个内联source-map
hidden-source-map:外部
错误代码错误原因,但是没有错误位置
不能追踪源代码错误,只能提示到构建后代码的错误位置
eval-source-map:内联
每一个文件都生成对应的source-map,都在eval
错误代码准确信息 和 源代码的错误位置
nosources-source-map:外部
错误代码准确信息, 但是没有任何源代码信息
cheap-source-map:外部
错误代码准确信息 和 源代码的错误位置
只能精确的行
cheap-module-source-map:外部
错误代码准确信息 和 源代码的错误位置
module会将loader的source map加入
总结:
开发环境建议:eval-source-map
eval-cheap-module-souce-map
生产环境建议:source-map
cheap-module-souce-map
在生产环境中,如果只想定位行数,又不想暴露源码,可以 使用 nosources-source-map
一般在生产环境中,都是建议直接关闭 source-map
3.4 oneOf
正常来讲,一个文件只能被一个loader处理,当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:先执行 eslint 在执行babel
const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production';
// 复用loader
const commonCssLoader = [
MiniCssExtractPlugin.loader,
'css-loader',
{
// 还需要在package.json中定义browserslist
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [require('postcss-preset-env')()]
}
}
];
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
// 在package.json中eslintConfig --> airbnb
test: /\.js$/,
exclude: /node_modules/,
// 优先执行
enforce: 'pre',
loader: 'eslint-loader',
options: {
fix: true
}
},
{
// 以下loader只会匹配一个
// 注意:不能有两个配置处理同一种类型文件
oneOf: [
{
test: /\.css$/,
use: [...commonCssLoader]
},
{
test: /\.less$/,
use: [...commonCssLoader, 'less-loader']
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: {version: 3},
targets: {
chrome: '60',
firefox: '50'
}
}
]
]
}
},
{
test: /\.(jpg|png|gif)/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
outputPath: 'imgs',
esModule: false
}
},
{
test: /\.html$/,
loader: 'html-loader'
},
{
exclude: /\.(js|css|less|html|jpg|png|gif)/,
loader: 'file-loader',
options: {
outputPath: 'media'
}
}
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseWhitespace: true,
removeComments: true
}
})
],
mode: 'production'
};
3.5 缓存
3.5.1 babel 缓存
让第二次打包构建速度更快,设置 cacheDirectory: true
关闭缓存文件压缩,cacheCompression:false
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: { version: 3 },
targets: {
chrome: '60',
firefox: '50'
}
}
]
],
// 开启babel缓存-优化构建效率, 第二次构建时,会读取之前的缓存
cacheDirectory: true,
// 关闭缓存文件压缩
cacheCompression:false
}
},
3.5.2 ESLint 缓存
plugins: [
new ESLintPlugin({
// 指定检查文件的根目录
context: path.resolve(__dirname, "src"),
// 排除 node_modules 中js文件的检查
exclude: 'node_modules', // 默认值
// 开启缓存
cache: true,
// eslint 缓存 存储的目录位置
cacheLocation: path.resolve(__dirname, './node_modules/.cache/eslintcache')
}),
],
3.5.3 文件资源缓存
hash
每次wepack构建时会生成一个唯一的hash值
但是因为js和css同时使用一个hash值,如果重新打包,会导致所有缓存失效
chunkhash
根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样
js和css的hash值还是一样的,因为css是在js中被引入的,所以同属于一个chunk
contenthash-推荐
根据文件的内容生成hash值。不同文件hash值一定不一样
让代码上线运行缓存更好使用
3.5.6 runtimeChunk
当我们修改 math.js 文件再重新打包的时候,因为 contenthash 原因,math.js 文件 hash 值发生了变化(这是正常的)
但是 index.js 文件的 hash 值也发生了变化,这会导致 index.js 的缓存失效。
解决方案:将 hash 值单独保管在一个 runtime 文件中
我们最终输出三个文件:index、math、runtime。当 math 文件发送变化,变化的是 math 和 runtime 文件,index不变。
runtime 文件只保存文件的 hash 值和它们与文件关系,整个文件体积就比较小,所以变化重新请求的代价也小
optimization: {
splitChunks: {
chunks: 'all',
// 缓存第三方库 设置
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
// 提取runtime文件
runtimeChunk: {
name: (entrypoint) => `runtime~${entrypoint.name}`, // runtime文件命名规则
},
},
3.6 tree shaking
主要功能是:去除无用代码,减少代码体积
但是主要前提是:1、 必须使用ES6模块化 2、 开启production环境
在package.json中配置 sideEffects:
"sideEffects": false 所有代码都没有副作用(都可以进行tree shaking),但是 也会把 css 或者 @babel/polyfill 等文件去掉,所以,一般可以采用下面的代码设置
"sideEffects": ["*.css", "*.less"]
webpack5 基本已经内置配置优化了 tree shaking
3.7 代码分割 code split
3.7.1 设置多入口
可以将入口 设置为 对象的方式,可以设置多个入口文件
entry: {
// 多入口:有一个入口,最终输出就有一个bundle
index: './src/js/index.js',
test: './src/js/test.js'
},
3.7.2 设置 optimization splitChunks
// 抽离 第三方
splitChunks: {
chunks: 'all',
// 缓存第三方库 设置
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors/vendors',
chunks: 'all',
},
},
},
单入口
1、 可以自动将node_modules中代码单独打包一个chunk最终输出
2、 可以 将 import 动态引入的模块 自动单独打包
动态导入 import
import动态导入语法:能将某个文件单独打包
test.js 文件:通过js代码,让某个文件被单独打包成一个chunk
// webpackChunkName: 'test' 魔法注释,设置 模块的名称
import(/* webpackChunkName: 'test' */'./test')
.then(({ sum }) => {
console.log('文件加载成功',sum(2,3));
})
.catch(() => {
console.log('文件加载失败~');
});
多入口
可以自动分析多入口chunk中,有没有公共的文件。如果有会打包成单独一个chunk
3.8 懒加载和预加载
懒加载: 当文件需要时才按需加载(告诉浏览器立即加载资源)
预加载:prefetch 等其他资源加载完毕,浏览器空闲了,再加载资源(告诉浏览器在空闲时才开始加载资源)
Preload
加载优先级高,Prefetch
加载优先级低。Preload
只能加载当前页面需要使用的资源,Prefetch
可以加载当前页面资源,也可以加载下一个页面需要使用的资源
console.log('index.js文件被加载了~');
document.getElementById('btn').onclick = function() {
import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) => {
console.log(mul(4, 5));
});
};
懒加载 preload 和 预加载 prefetch 有浏览器兼容性问题
3.9 dll
使用 DllPlugin 为更改不频繁的代码生成单独的编译结果。这可以提高应用程序的 编译速度,尽管它增加了构建过程的复杂度
webpack.dll.js
/*
使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包
当你运行 webpack 时,默认查找 webpack.config.js 配置文件
需求:需要运行 webpack.dll.js 文件
--> webpack --config webpack.dll.js
*/
const { resolve } = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
// 最终打包生成的[name] --> jquery
// ['jquery'] --> 要打包的库是jquery
jquery: ['jquery'],
},
output: {
filename: '[name].js',
path: resolve(__dirname, 'dll'),
library: '[name]_[hash]' // 打包的库里面向外暴露出去的内容叫什么名字
},
plugins: [
// 打包生成一个 manifest.json --> 提供和jquery映射
new webpack.DllPlugin({
name: '[name]_[hash]', // 映射库的暴露的内容名称
path: resolve(__dirname, 'dll/manifest.json') // 输出文件路径
})
],
mode: 'production'
};
webpack.config.js
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname, 'build')
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
// 告诉webpack哪些库不参与打包,同时使用时的名称也得变~
new webpack.DllReferencePlugin({
manifest: resolve(__dirname, 'dll/manifest.json')
}),
// 将某个文件打包输出去,并在html中自动引入该资源
new AddAssetHtmlWebpackPlugin({
filepath: resolve(__dirname, 'dll/jquery.js')
})
],
mode: 'production'
};
3.10 Include/Exclude
开发时我们需要使用第三方的库或插件,所有文件都下载到 node_modules 中了。而这些文件是不需要编译可以直接使用的。
所以我们在对 js 文件处理时,要排除 node_modules 下面的文件
include:包含,只处理 xxx 文件
exclude:排除,除了 xxx 文件以外其他文件都处理
二者不可以同时
{
test: /\.js$/,
// exclude: /node_modules/, // 排除node_modules代码不编译
include: path.resolve(__dirname, "../src"), // 也可以用包含
loader: "babel-loader",
},
4 其他配置
4.1 resolve
// 解析模块规则的配置
resolve:{
// 配置解析模块路径的别名,可以极大的简化路径的书写,但是就不会有路径提示了
alias:{
'@':path.resolve(__dirname,'src')
},
// 配置省略路径文件的后缀名
extensions:['.js','.json','.jsx']
// 配置解析模块寻找的指定目录
modules:[path.resolve(__dirname,'../../node_modules')]
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具