webpack文档链接
1.webpack五大核心
- Entry - 入口
Entry 指示 Webpack 从哪个模块开始构建内部依赖图,以及如何将所有依赖项连接在一起。
- Output - 输出
Output 定义了 Webpack 构建的输出文件的位置和文件名。
- Loader - 加载器
Loader 用于对模块的源代码进行转换,以便于 Webpack 处理。例如,当 Webpack 遇到一个 “.css” 文件时,使用 “css-loader” 和 “style-loader” 将其转换为 JavaScript 代码以便于在浏览器中呈现样式。
- Plugin - 插件
Plugin 在 Webpack 的构建过程中扮演一个关键的角色,它可以完成各种各样的任务,例如压缩代码、拷贝文件到输出目录、创建全局变量等等。
- Mode - 模式
Mode 是 Webpack 4 引入的一个新特性,它提供了三种不同的构建模式,即 “development”、“production” 和 “none”,分别对应着开发模式、生产模式和不设置模式。
以下是webpack.config.js中的配置
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
module.exports = {
// Entry
entry: './src/index.js', // 指定入口文件
// Output
output: {
filename: 'bundle.js', // 输出文件名
path: path.resolve(__dirname, 'dist'), // 输出目录
clean: true, // 在构建前清空输出目录,Webpack 5+ 新增
},
// Mode
mode: 'production', // 设置模式为生产模式
// Module
module: {
rules: [
{
test: /\.css$/, // 正则表达式匹配所有.css文件
use: ['style-loader', 'css-loader'], // 使用style-loader和css-loader处理CSS文件
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/, // 匹配图片文件
type: 'asset/resource', // 使用 asset 模块处理图片
},
{
test: /\.js$/, // 匹配 JavaScript 文件
exclude: /node_modules/, // 排除 node_modules 目录
use: {
loader: 'babel-loader', // 使用 Babel 加载器转换 ES6 到 ES5
options: {
presets: ['@babel/preset-env'],
},
},
},
// 示例:为.js文件启用ESLint(可选)
// {
// test: /\.js$/,
// exclude: /node_modules/,
// use: {
// loader: 'eslint-loader',
// options: {
// // eslint options (if necessary)
// },
// },
// },
],
},
// Plugins
plugins: [
// 创建一个HTML文件来承载所有生成的bundle
new HtmlWebpackPlugin({
template: './src/index.html', // 使用src目录下的index.html作为模板
}),
// 示例:优化JS和CSS文件(可选,视需求而定)
// 在生产模式下,Webpack 会自动应用这些优化,但也可以显式定义
// new TerserPlugin({
// terserOptions: {
// // terser options (if necessary)
// },
// }),
// new CssMinimizerPlugin(),
],
// Optimization
optimization: {
minimize: true, // 开启代码压缩
minimizer: [
// 使用自定义的minimizer配置
// 可以注释掉,因为Webpack默认已经配置好了
// new TerserPlugin({
// // terser options
// }),
// new CssMinimizerPlugin(),
],
},
// DevServer(开发服务器配置,通常只在开发模式下使用)
// 注意:此配置不包含在mode设置中
devServer: {
contentBase: './dist', // 告诉服务器从哪里提供内容
hot: true, // 启用热模块替换
contentBase: path.join(__dirname, 'dist'), // 设置静态文件根目录
compress: true, // 开启 gzip 压缩
port: 9000, // 设置端口号
},
};
// 注意:在开发模式下,你可能需要调整mode为'development',并可能移除或调整optimization中的minimize选项
2.谈谈你对Webpack的理解(Webpack是什么?)
Webpack 是一个 静态模块打包器,可以分析各个模块的依赖关系,项目中的所有资源皆为模块,通过分析模块间的依赖关系,在其内部递归构建出一个依赖关系图,其中包含应用程序需要的每个模块,然后将这些模块打包成一个或多个 bundle。最终编绎输出模块为 HTML、JavaScript、CSS 以及各种静态文件(图片、字体等)。
Webpack 的工作流程大致如下:
- Webpack 从配置的入口点开始,分析应用程序的依赖关系。
- Webpack 使用加载器(loaders)将非 JavaScript 文件转换为模块。
- Webpack 递归地构建一个依赖关系图,该图包含了应用程序需要的每个模块。
- Webpack 将所有这些模块打包成一个或多个 bundle。
- 在最后一步,Webpack 可以将这些 bundle 输出到文件系统上。
3. Webpack的打包过程/打包原理/构建流程?
- 初始化:启动构建,读取与合并配置参数,加载plugin,实例化Compiler
- 编译:从Entry出发,针对每个Module串行调用对应的Loader去翻译文件中的内容,再找到该Module依赖的Module,递归的进行编译处理
- 输出:将编译后的Module组合成Chunk,将Chunk转换成文件,输出到文件系统中
4. loader的作用(常见的loader有哪些)
作用
webpack中的loader是一个函数,主要为了实现源码的转换,所以loader函数会以源码作为参数,比如,将ES6转换为ES5,将less转换为css,然后再将css转换为js,以便能嵌入到html文件中。
默认情况下,webpack只支持对js和json文件进行打包,但是像css、html、png等其他类型的文件,webpack则无能为力。因此,就需要配置相应的loader进行文件内容的解析转换。
有哪些常见的Loader?他们是解决什么问题的?
- image-loader:加载并且压缩图片文件。
- less-loader:加载并编译 LESS 文件。
- sass-loader:加载并编译 SASS/SCSS 文件。
- css-loader:加载 CSS,支持模块化、压缩、文件导入等特性,使用css-loader必须要配合使用style-loader。
- style-loader:用于将 CSS 编译完成的样式,挂载到页面的 style 标签上。需要注意 loader 执行顺序,style-loader 要放在第一位,loader 都是从后往前执行。
- babel-loader:把 ES6 转换成 ES5
- postcss-loader:扩展 CSS 语法,使用下一代 CSS,可以配合 autoprefixer 插件自动补齐 CSS3 前缀。
- eslint-loader:通过 ESLint 检查 JavaScript 代码。
- vue-loader:加载并编译 Vue 组件。
- file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件 (处理图片和字体)
- url-loader:与 file-loader 类似,区别是用户可以设置一个阈值,大于阈值会交给 file-loader 处理,小于阈值时返回文件 base64 形式编码 (处理图片和字体)。
- source-map-loader:加载额外的 Source Map 文件,以方便断点调试。
5. plugin的作用(常见的plugin有哪些)
作用
webpack中的plugin赋予其各种灵活的功能,例如打包优化、资源管理、环境变量注入等,它们会运行在webpack的不同阶段(钩子 / 生命周期),贯穿了webpack整个编译周期。目的在于解决 loader 无法实现的其他事。
举例说明(代码)
HtmlWebpackPlugin
- 作用:自动生成HTML文件,并将打包后的文件(如JavaScript、CSS)自动引入。这对于单页应用(SPA)特别有用,可以保证生成的HTML文件总是引用最新的打包文件。
- 示例:在Webpack配置文件中配置HtmlWebpackPlugin,指定模板文件后,Webpack会自动生成一个包含打包后文件引用的HTML文件。
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// 其他配置...
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html', // 指定模板文件
filename: 'index.html', // 输出的HTML文件名
}),
],
};
TerserWebpackPlugin
作用:压缩JavaScript代码,删除无用的空格、注释和变量名,以减少文件体积,提高加载速度
示例:在Webpack配置文件中配置TerserWebpackPlugin后,Webpack会在构建过程中自动压缩打包后的JavaScript文件。
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
// 其他配置...
optimization: {
minimize: true,
minimizer: [new TerserPlugin({
// TerserPlugin 选项...
})],
},
};
CleanWebpackPlugin
作用:在每次构建前清空输出目录,确保每次构建都是基于一个干净的环境,避免旧文件残留导致的问题。
示例:在Webpack配置文件中配置CleanWebpackPlugin后,每次运行构建命令时,该插件都会先清空指定的输出目录,然后再进行构建。
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
// 其他配置...
plugins: [
new CleanWebpackPlugin(), // 默认会清理`output.path`指定的目录
// 或者你可以传递一个配置对象来自定义行为
// new CleanWebpackPlugin({
// cleanOnceBeforeBuildPatterns: ['**/*', '!.gitkeep'], // 自定义清理模式
// }),
],
};
CopyWebpackPlugin
作用:复制静态资源文件(如图片、字体文件等)到指定的输出目录。这对于需要将非代码资源也打包到发布目录中的场景非常有用。
示例:在Webpack配置文件中配置CopyWebpackPlugin,并指定需要复制的资源和目标目录后,Webpack会在构建过程中自动将指定资源复制到目标目录。
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
// 其他配置...
plugins: [
new CopyWebpackPlugin({
patterns: [
{ from: 'source', to: 'dest' }, // 将'source'目录下的所有内容复制到'dest'目录
// 你可以指定更复杂的模式,包括glob模式等
],
}),
],
};
有哪些常见的Plugin?他们是解决什么问题的?
- html-webpack-plugin:可以复制一个有结构的html文件,并自动引入打包输出的所有资源(JS/CSS)
- clean-webpack-plugin:重新打包自动清空 dist 目录
- mini-css-extract-plugin:提取 js 中的 css 成单独文件
- optimize-css-assets-webpack-plugin:压缩css
- uglifyjs-webpack-plugin:压缩js
- commons-chunk-plugin:提取公共代码
- define-plugin:定义环境变量
6.Webpack中Loader和Plugin的区别
运行时机
- 1.loader运行在编译阶段
- 2.plugins 在整个周期都起作用
使用方式
Loader:1.下载 2.使用
Plugin:1.下载 2.引用 3.使用
loader是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的文件中;plugin赋予了webpack各种灵活的功能,例如打包优化、资源管理、环境变量注入等,目的是解决 loader无法实现的其他事。
在运行时机上,loader 运行在打包文件之前;plugin则是在整个编译周期都起作用。
在配置上,loader在module.rules中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(类型文件)、loader、options (参数)等属性;plugin在 plugins中单独配置,类型为数组,每一项是一个 plugin 的实例,参数都通过构造函数传入。
7.webpack的热更新是如何做到的?说明其原理
热更新的核心就是客户端从服务端拉去更新后的文件,准确的说是 chunk diff (chunk 需要更新的部分),实际上webpack-dev-server与浏览器之间维护了一个websocket,当本地资源发生变化时,webpack-dev-server会向浏览器推送更新,并带上构建时的hash,让客户端与上一次资源进行对比。客户端对比出差异后会向webpack-dev-server发起 Ajax 请求来获取更改内容(文件列表、hash),这样客户端就可以再借助这些信息继续向webpack-dev-server发起 jsonp 请求获取该chunk的增量更新。
简单理解:Webpack 的热更新(Hot Module Replacement, HMR)是一种在开发过程中实时更新模块而不需刷新整个页面的技术。这对于提高开发效率非常有帮助,因为它允许开发者在编辑代码后立即看到结果,而不会丢失任何页面状态,比如表单数据或用户交互。
下面是以简单易懂的方式解释 HMR 的工作原理:
-
WebSocket 建立连接: 当你启动 Webpack Dev Server 并开启 HMR 功能时,它会创建一个 WebSocket 连接。这个连接允许 Dev Server 和浏览器之间进行实时双向通信。
-
监听文件变化: Webpack Dev Server 会监听项目中的文件变化。当你修改了一个文件并保存时,Dev Server 会检测到这个变化。
-
增量构建: Webpack 不会重新构建整个项目,而是只构建那些发生改变的模块以及它们的依赖。这样可以显著减少构建时间。
-
发送更新信息: 构建完成后,Dev Server 通过 WebSocket 发送一个消息给浏览器,告诉它哪些模块已经被更新。
-
浏览器接收更新: 浏览器接收到这个消息后,知道哪些模块需要替换。它会卸载旧的模块,并加载新的模块。
-
模块替换: 浏览器会用新模块替换掉旧模块,但是不会刷新整个页面。这通常意味着只有改动的部分会被更新,而其他部分保持不变。
-
保留状态: 由于模块是在运行时被替换的,所以像组件的状态、变量的值等页面状态会被保留下来,用户不会感觉到页面有任何中断。
// 插件配置
plugins: [
// 如果你想使用 HMR,通常需要引入 HotModuleReplacementPlugin
new webpack.HotModuleReplacementPlugin(),
],
8.如何解决循环依赖问题
循环依赖是指两个或更多的模块直接或间接地相互引用对方,形成一个闭环的依赖关系。这种情况下,Webpack 等模块打包工具可能会遇到问题,因为它们无法确定正确的加载顺序。
原始代码示例(存在循环依赖)
当前为A文件
// 引入模块B
import B from './B';
export default class A {
constructor() {
console.log('A is created');
this.b = new B(); // 使用模块B
}
}
当前为B文件
// 引入模块A
import A from './A';
export default class B {
constructor() {
console.log('B is created');
this.a = new A(); // 使用模块A
}
}
可安装插件查找哪个出现了循环
1.终端安装 npm i circular-dependency-plugin -D
在 webpack 配置文件里添加 plugins 配置。
const CircularDependencyPlugin = require('circular-dependency-plugin')
export default {
...,
plugins: [
...,
new CircularDependencyPlugin(
{
exclude: /node_modules/,
include: /src/,
failOnError: false,
allowAsyncCycles: false,
cwd: process.cwd(),
}
),
]
}
重启项目,在启动命令行里就能看到循环引用的警告信息,插件会帮你定位出问题的文件路径。
如何解决
创建一个C文件来放置共享数据
// 这里可以放置一些共享的数据或函数
export default class C {
// 示例方法或数据
sharedData = 'This is shared data';
}
A文件中引用C
import C from './C';
export default class A {
constructor(c) {
console.log('A is created');
this.c = c; // 使用模块C
}
}
B文件中引用C
import C from './C';
export default class B {
constructor(c) {
console.log('B is created');
this.c = c; // 使用模块C
}
}
9. 如何提高Webpack构建速度
1. 使用Webpack自带的缓存
Webpack 4+ 引入了文件系统缓存,可以在webpack.config.js
中启用
module.exports = {
// ...
cache: {
type: 'filesystem', // 启用文件系统缓存
buildDependencies: {
config: [__filename] // 当webpack.config.js改变时,缓存失效
}
},
// ...
};
2.使用thread-loader,cache-loader
进行并行处理
thread-loader的主要目的是利用多核处理器的能力,通过将加载器的执行分发到多个工作线程中来加速构建过程。这对于 I/O 密集型或 CPU 密集型的任务特别有用,比如编译 TypeScript、Babel 转换、Sass 编译等。thread-loader
可以显著减少构建时间,尤其是在大型项目中。
npm install --save-dev thread-loader
module: {
rules: [
{
test: /\.js$/,
use: [
'thread-loader', // 放在耗时的loader之前
'babel-loader'
]
}
]
}
cache-loader 的目标是避免重复执行相同的加载器任务。它会缓存加载器的结果,当同一个模块再次被加载时,如果缓存有效,cache-loader
将直接从缓存中读取结果,而不是重新执行加载器。这对于频繁构建的场景非常有帮助,因为它减少了不必要的计算和 I/O 操作。例如单页面使用
npm install --save-dev cache-loader
module.exports = {
// ...
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: [
'cache-loader', // 添加 cache-loader
'babel-loader',
],
},
],
},
};
一般情况下,推荐同时使用 thread-loader
和 cache-loader
。thread-loader
放在 cache-loader
之后,这样可以先检查缓存,如果缓存有效则直接使用,否则再利用多线程加速加载器的执行。
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: [
'cache-loader', // 缓存加载器结果
{
loader: 'thread-loader', // 使用多线程
options: {
workers: require('os').cpus().length - 1 // 根据 CPU 核心数量设置工作线程数
}
},
'babel-loader' // 实际的代码转换加载器
]
}
3. 减少解析和编译时间
通过配置 resolve.alias
和 resolve.extensions
来优化模块解析,使用 babel-loader
的 cacheDirectory
选项来缓存编译结果
module.exports = {
// ...
resolve: {
alias: {
components: path.resolve(__dirname, 'src/components'),
utils: path.resolve(__dirname, 'src/utils'),
},
extensions: ['.js', '.jsx', '.json'],
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true, // 开启缓存
},
},
},
],
},
};
4.压缩和最小化
- Webpack 4+ 默认使用
TerserPlugin
进行JavaScript压缩,但如果你需要自定义配置,可能需要显式安装terser-webpack-plugin
。 - 对于CSS,
MiniCssExtractPlugin
需要单独安装,以及可能的cssnano
(如果用于进一步压缩CSS)。
npm install --save-dev terser-webpack-plugin mini-css-extract-plugin css-loader cssnano postcss-loader
(注意:cssnano
通常作为PostCSS插件使用,因此需要postcss-loader
)
const TerserPlugin = require('terser-webpack-plugin'); // 如果需要自定义配置
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
optimization: {
minimize: true,
minimizer: [new TerserPlugin(/* options */)], // 如果需要自定义配置
},
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] // 使用cssnano需要在postcss-loader中配置
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[id].css',
}),
],
};
5.压缩Node.js内存
不是Webpack配置,但相关
这不是Webpack的直接配置,但你可以通过调整Node.js的内存限制来影响Webpack的性能。这通常通过在命令行中设置--max-old-space-size
参数来完成,不需要额外下载包。
node --max-old-space-size=4096 ./node_modules/.bin/webpack --config webpack.config.js
6.代码压缩
JS 压缩
webpack 4.0默认在生产环境的时候是支持代码压缩的,即mode=production模式下。实际上webpack 4.0默认是使用terser-webpack-plugin这个压缩插件,在此之前是使用 uglifyjs-webpack-plugin,两者的区别是后者对 ES6 的压缩不是很好,同时我们可以开启 parallel参数,使用多进程压缩,加快压缩。
CSS 压缩
CSS 压缩通常是去除无用的空格等,因为很难去修改选择器、属性的名称、值等。可以使用另外一个插件:css-minimizer-webpack-plugin。
HTML 压缩
使用HtmlWebpackPlugin插件来生成 HTML 的模板时候,通过配置属性minify进行 html 优化。
module.exports = {
plugin:[
new HtmlwebpackPlugin({
minify:{
minifyCSS: false, // 是否压缩css
collapseWhitespace: false, // 是否折叠空格
removeComments: true // 是否移除注释
}
})
]
}
7. 图片压缩
配置image-webpack-loader
8. Tree Shaking
Tree Shaking是一个术语,在计算机中表示消除死代码,依赖于 ES Module 的静态语法分析(不执行任何的代码,可以明确知道模块的依赖关系)。在webpack实现Tree shaking有两种方案:
usedExports:通过标记某些函数是否被使用,之后通过 Terser 来进行优化的
module.exports = {
...
optimization:{
usedExports
}
}
使用之后,没被用上的代码在webpack打包中会加入unused harmony export mul注释,用来告知Terser在优化时,可以删除掉这段代码。
sideEffects:跳过整个模块/文件,直接查看该文件是否有副作用
sideEffects用于告知webpack compiler哪些模块时有副作用,配置方法是在package.json中设置sideEffects属性。如果sideEffects设置为false,就是告知webpack可以安全的删除未用到的exports。如果有些文件需要保留,可以设置为数组的形式,如:
"sideEffecis":[
"./src/util/format.js",
"*.css" // 所有的css文件
]
9. 缩小打包域
排除webpack不需要解析的模块,即在使用loader的时候,在尽量少的模块中去使用。可以借助 include和exclude这两个参数,规定loader只在那些模块应用和在哪些模块不应用。
10. 减少 ES6 转为 ES5 的冗余代码
使用、bable-plugin-transform-runtime插件
11. 提取公共代码
通过配置CommonsChunkPlugin插件,将多个页面的公共代码抽离成单独的文件
12. 其他
组件懒加载、路由懒加载、开启gzip、公共的第三方包上cdn、配置cache缓存Loader对文件的编译副本、配置resolve提高文件的搜索速度(@: src)