一.为什么需要构建工具
1.转换ES6
2.转化jsx语法
3.CSS前缀补全/预处理器
4.文件压缩混淆
5.图片压缩
二.webpack配置文件名称及组成
webpack默认配置文件:webpack.config.js,可以通过webpack --config指定哪个文件为配置文件
module.exports={
entry:'./src/index.js', --------------①默认打包文件的入口文件
output:'./dist/main.js', -------------②默认打包的输出
mode:'production', -------------------③指定环境
module:{
rules:[---------------------------④Loader配置
{test:/\.txt$/,use:'raw-loader'}
]
},
plugins:[-----------------------------⑤插件配置
new HtmlwebpackPlugin({
template:'./src/index.html',
})
]
}
三.配置webpack
3.1 Entry
在webpack环境中,代码,非代码比如图片、字体、文件都会作为一个模块,被webpack打包,打包依赖的入口就是entry
//单入口:entry是一个字符串,适合单页面应用
module.export={
entry:'./src/app.js'
}
//多入口:entry是一个对象,适合多页面应用
module.export={
entry:{
app:'./src/app.js',
adminApp:'./src/adminApp.js'
}
}
3.2 Output
output用来告诉webpack如何将编译后的文件输出到哪个磁盘,哪个目录,文件名称
const path = require('path')
module.exports={
output:{
path:path.join(__dirname,'dist') ,
filename:'bundle.js'
},
}
//表示输出到当前文件所属目录下的dist文件夹中,文件名叫bundle.js
__dirname 可以用来动态获取当前文件所属目录的绝对路径
__filename 可以用来动态获取当前文件的绝对路径,包含当前文件
测试 test.js 文件文件路径为: E:\前端相关\demo\test.js
console.log('__dirname->', __dirname)
console.log('__filename->', __filename)
// 输出: __dirname-> E:\前端相关\demo
// 输出: __filename-> E:\前端相关\demo\test.js
3.3 Loaders(实质是一个转换器,运行在打包之前)
webpack开箱即用,只支持js和json两种文件类型,通过Loaders去支持其他文件类型并且把它们转化成有效的模块,添加到打包文件中。webpack只能打包,相关的编译工作需要loader。Loaders本身是一个函数,接收源文件作为参数,返回转化的结果,使用时需要安装,因为loader不是webpakc内置的,属于第三方开发。一句话概括就是loader使得webpack可以处理各种类型的文件。常用的loader如下图
| 名称 | 描述 |
|---|---|
| babel-loader | 转换ES6,ES7等js新特性语法 |
| csss-loader | 支持.css文件的加载和解析 |
| less-loader | 将less文件转成css文件 |
| ts-loader | 将TS文件转成js |
| file-loader | 进行图片、字体的打包 |
| raw-loader | 将文件以字符串的形式导入 |
| thread-loader | 多进程打包js和css |
module.exports={
module:{
rules:[
{test:/\.txt$/,use:'raw-loader'},
]
}
}
//loaders配置在module模块中rules里面,rules是一个数组,可以配置多个loader
//每一个loader,test作用是指定匹配规则,use指定使用什么loader处理。上面的配置代表看到以".txt"结尾的文件,交给raw-loader处理,处理完之后再交给webpack打包。
3.4 Plugins
Plugin用于扩展webpack的功能,如打包优化,资源管理,环境变量注入。在webpack运行的生命周期中,会广播出许多事件,plugin可以监听这些事件,在合适的时机通过webpack提供的api改变输出结果。Plugins在webpack整个编译周期都起作用。Plugin有的是webpack内置,有的需要额外安装。 常用的Plugins如下
| 名称 | 描述 |
|---|---|
| CommonsChunkPlugin | 将chunks相同的模块代码提取成公共的js |
| CleanWebpackPlugin | 清除构建目录 |
| ExtractTextWebpackPlugin | 将css从bundle文件中提取成一个独立的css文件 |
| CopyWebpackPlugin | 将文件或者文件夹拷贝到构建的输出目录 |
| HtmlWebpackPlugin | 帮助在dist文件夹下创建html并且自动引入bundle.js |
| UglifyjsWebpackPlugin | 压缩js |
| ZipWebpackPlugin | 将打包出的资源生成一个zip包 |
module.exports={
plugins:[
new HtmlwebpackPlugin({
template:'./src/index.html',
})
]
}
//将配置放到plugins数组里
3.5 Mode(webpack4之前没有)
用来指定当前的构建环境是:production、development还是none,默认值是production,设置mode可以使用webpack内置的函数。比如mode设置为production,webpack会开启生产阶段的参数,以及插件的功能。mode设置为development,webpack会开启开发阶段的参数,以及插件的功能。
| 选项 | 描述 |
|---|---|
| development | 设置process.env.NODE_ENV的值为development,开启NamedChunksPlugin和NamedModulesPlugin |
| production | 设置process.env.NODE_ENV的值为production,开启FlagDependencyUsagePlugin,FlagIncludedChunksPlugin,ModuleConcatenationPlugin,NoEmitOnErrorsPlugin,OccurenceOrderPlugin,SideEffectsFlagPlugin和TerserPlugin |
| none | 不开启任何优化选项 |
知识拓展
1.process 是 node 的全局变量,`process.env` 是一个环境对象,其中保存着系统的环境的变量
信息,但是process.env没有NODE_ENV这个属性。
2.NODE_ENV变量并不是 process.env 直接就有的,而是通过我们设置得到的。
3.可以通过process.env.NODE_ENV这个变量区分开发环境或生产环境。
4.当使用process.env.NODE_ENV=production, 来设置环境变量时,大多数Windows系统将会阻塞
报错,原因是windows不支持NODE_ENV=production的设置方式,解决:使用`cross-env`。
cross-env可以运行跨平台设置和使用环境变量的脚本,提供一个设置环境变量的scripts,帮助设置
环境变量,然后在windows上兼容运行。
{ "scripts":
{ "build": "cross-env NODE_ENV=production webpack --config webpack.config.js" }
}
3.6 Source Map
Source Map是一个信息文件,里面存储着位置信息,Source Map文件中存储着压缩混淆后的代码所对应转化前的位置信息,开启后出错的时候,将直接显示原始代码,方便调试。
const isEnvProduction = process.env.NODE_ENV === 'production'
module.exports={
mode:'development',
//eval-source-map仅限在“开发模式”下使用,不建议在“生产模式”下使用
//此选项生成的Source Map能够保证“运行报错行数”和“源代码行数”一致
//nosource-source-map在生产环境能看到“源代码报错行数”,看不到源码。
//source-map既能看到“源码报错行数”,也可以看到源码,最不安全。
devtool:isEnvProduction?'nosource-source-map':'eval-source-map',
}
3.7 路径参数@
module.exports={
mode:'development',
resolve:{
alias:{
//@代表src这一层
'@':path.join(__dirname,'src')
}
}
}
配置完之后,webpack会自动解析@,找到src这一层,但是当输入@,vscode不认识,所以不会自动提示src下级文件目录,需要在src目录下新建jsconfig.json配置文件
{
"compilerOptions":{
"baseUrl":".",
"paths":{
"@/*":["./src/*"]
}
},
"include":["src/**/*"], ----代表提示src目录下的
"exclude":["node_modules","public","build"]----排除掉这些目录
}
四. 文件监听及热更新原理
4.1文件监听
webpack会轮询判断文件的最后编辑时间是否变化,每个文件发生了变化,并不会立刻告诉监听者,而是先缓存起来,等aggregateTimeout(默认300ms)在执行。
module.export={
watch:true,//开启监听文件
watchOptions:{//开启监听模式,watchOptions才有意义
ignored:/node_modules/,//不监听的文件
aggregateTimeout:300,//监听到变化后等300ms再去执行,默认300ms
poll:1000,//轮询判断文件是否变化,默认每秒问1000次
}
}
//这种方式的缺陷是每次构建的文件都放进本地磁盘文件中,效率不高,而且不会自动帮助刷新浏览器。
4.2热更新的原理
HMR全称 Hot Module Replacement,模块热替换,指在应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个应用。
wepack Compile:webpack创建的compile编译器实例。
HMR Server:作用是将热更新的文件输出给HMR Rumtime
Bundle Server:提供编译的文件资源,供浏览器访问
HMR Rumtime:被注入到浏览器,更新文件的变化
HMR Server工作在服务器端,HMR Rumtime工作在客户端,两者之间建立socket连接。
热更新的过程启动阶段是①②AB,compiler编译器对文件进行编译打包,然后传输给Bundle Server为浏览器提供服务,可以访问到bundle.js。
文件更新阶段是①②③④⑤,文件系统检测到文件变化,由compiler编译器编译打包,然后交给HMR Server,HMR Server会立即通知HMR Rumtime更新我们的代码。
热更新过程中webapck-dev-server和webapck-dev-middleware做了哪些事。
webpack-dev-server只负责启动服务和前置准备工作,通过webpack-dev-server创建两个服务器:提供静态资源的服务Bundle Server和HMR Server服务,Bundle Server 负责直接提供静态资源的服务,打包后的资源直接被浏览器请求和解析。
webapck-dev-middleware做了什么
(1)调用了compiler.watch方法,这个方法开启对本地文件的监听,当文件发生变化,重新编译,编译完成之后继续监听。
(2)执行setFs方法,这个方法主要目的就是将编译后的文件打包到内存。这就是为什么在开发的过程中,你会发现dist目录没有打包后的代码,因为都在内存中。原因就在于访问内存中的代码比访问文件系统中的文件更快,而且也减少了代码写入文件的开销,这一切都归功于memory-fs
关于webpack热模块更新的总结如下:
- 通过webpack-dev-server创建两个服务器:提供静态资源的服务(express)和Socket服务
- express server 负责直接提供静态资源的服务(打包后的资源直接被浏览器请求和解析)
- socket server 是一个 websocket 的长连接,双方可以通信
- 当 socket server 监听到对应的模块发生变化时,会生成两个文件.json(manifest文件)和.js文件(update chunk)
- 通过长连接,socket server 可以直接将这两个文件主动发送给客户端(浏览器)
- 浏览器拿到两个新的文件后,通过HMR runtime机制,加载这两个文件,并且针对修改的模块进行更新
五.React项目中配置文件重写问题
使用Creat-React-App创建的项目,默认webpack.config.js文件放在node_modules > react-scripts > config 文件夹下。想要修改配置文件有两种方法。
5.1 使用 eject
可以通过执行npm run eject,暴露出隐藏的配置文件,但是这种方式是不可逆的,不推荐
5.2 使用 craco
npm install @craco/craco --save-dev
//修改 package.json文件里的脚本命令
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test",
}
项目根目录新建craco.config.js 配置文件,重写配置
六.参考
面试官:说说Webpack的热更新是如何做到的?原理是什么?_动感超人,的博客-CSDN博客
轻松理解webpack热更新原理 - 掘金 (juejin.cn)
bilibili视频资源# Webpack 热更新原理与实战