前端工程化WebPack5️⃣
本篇:Blog代码公众号回复:WebPack 紧跟前文: WebPack5.0 快速入门 简单的了解了:WebPack的使用,接下来康康项目中的管理吧;
经过上述链接学习: 我们已经掌握了WebPack基本操作,优化之前的Demo:完善login登录页面,优化目录结构:
WebPack搭建开发环境:
为什么需要搭建开发环境😗❓ 经过上述,我们已经满足了模块打包的使用,
同时发现一个严重问题: 每次改动代码,都要重新打包,很麻烦❗❗ 那么如何解决呢❓
webpack-dev-server
Webpack-dev-server 是一个基于 Node.js 构建的轻量级开发服务器:
专为 Webpack 打包生成的资源文件提供服务: 它在本地开发环境中启动一个实时的 Web 服务器,极大地提高了开发效率;
-
实时重载: 当代码发生变化时,Webpack-dev-server 会自动重新编译并刷新浏览器,无需手动刷新;
-
热部署替换 (HMR): 允许在不刷新整个页面的情况下替换、添加或删除模块,保持应用状态;
-
静态文件服务: 可以为项目中的静态文件(如 HTML、CSS、图片等)提供服务;
-
支持配置代理: 将 API 请求转发到不同的服务器,解决跨域问题;
-
Gzip 压缩: 支持 Gzip 压缩,提高传输效率;
NPM安装软件包:
NPM安装软件包:webpack-dev-server 软件包到当前项目;
npm i webpack-dev-server --save-dev
配置 webpack.config.js
webpack-dev-server:仅用于开发环境,请 不要 在生产环境中使用它们!
//省略...
module.exports = {
mode: 'development', //开发模式
devServer: { //Webpack-dev-server配置
hot: true, //启用热模块替换功能
port: 9000, //指定开发服务器的端口号
compress: true, //启用 Gzip 压缩,提高传输效率
static: './dist', //指定静态文件的目录,./dist 目录下的文件将被作为静态资源提供服务;
},
//省略...
}
-
Webpack 中,
mode
配置选项用于指定构建的模式提供了三种模式:
development
开发模式)、production
生产模式)、`none无模式) -
devServer:{ Webpack 中的配置选项 }
: 用于配置 Webpack-dev-server 的行为,提供本地实时重载、热部署、功能;
在 package.json
中配置脚本:
webpack-dev-server
支持: 命令行设置配置,且优先级高于配置文件中的,推荐用命令行设置;
"scripts": {
"build": "webpack",
"dev": "webpack serve --port 5400 --mode=development --open"
},
"dev": "webpack serve --port 5400 --mode=development --open"
运行启动开发模式 自动打开浏览器 端口5400
NPM快速运行命令: npm run dev
webpack–dev-server:其原理是 通过在内存中创建虚拟文件系统来提供开发服务器功能;
监听内存中的./dist
目录,启动一个基于 Node.js 的服务器通常是 Express 服务器)
而Express服务器默认启动的是: public/index.html
页面,
因为:此案例没有index.html
,所以打开一个空页面: 可以通过配置定义默认开启页面;
定义一个index.html
页面,并默认跳转:login.html
首页:
定义一个index.html
页面,并默认跳转:login.html
首页:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// webpack-dev-server 的 Web 服务,自动跳转到我们规定的首页
location.href = '/login/index.html'
</script>
</body>
</html>
添加 webpack.config.js 模板映射配置: 实际开发过程中结合项目结构目录进行配置;
//WebPack配置: 省略...
module.exports = {
////省略...
plugins: [ //插件: 给Webpack提供更多功能;
new HtmlWebpackPlugin({
template: path.resolve(__dirname,'public/index.html'), // 模板文件
filename: path.resolve(__dirname,'dist/index.html'), // 输出文件
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname,'public/login.html'), // 模板文件
filename: path.resolve(__dirname,'dist/login/login.html'), // 输出文件
}),
new MiniCssExtractPlugin({
filename: './[name]/index.css' // 输出css文件目录
}),
],
}
WebPack打包模式:
模式名称 | 模式名字 | 特点 | 场景 |
---|---|---|---|
开发模式 | development | 调试代码,实时加载,模块热替换等 | 本地开发 |
生产模式 | production | 压缩代码,资源优化,更轻量等 | 打包上线 |
如何设置影响 Webpack呢?
-
①:在 webpack.config.js 配置文件设置 mode 选项
module.exports = { //mode: 'development' mode: 'production' }
-
②:定义启动命令,指定设置开发模式,命令行设置的优先级高于配置文件,支持在Package.JSON中定义
production:生产模式 压缩代码,资源优化,更轻量等;
development:开发模式 非压缩代码,调试代码,实时加载,模块热替换等;
"scripts": { "build": "webpack --mode=production", "dev": "webpack --mode=development", },
打包模式/环境切换:
在大型项目中,经常出现的需求,根据不同的环境而需要不同的配置: 如:
- 开发模式: 为了方便开发调试速度,代码压缩,通常CSS+JS压缩在一个文件,使用:
style-loader加载器
- 生产模式: 为了提高JS、CSS文件加载速度,方便文件管理,通常使用:
MiniCssExtractPlugin.loader加载器
如此:不同环境需要不同的配置如何,轻松便捷的实现环境切换呢?
方案一:配置导出函数
webpack.config.js配置导出函数::局限性大,只接受 2 种模式: 此处不详细介绍🔈
//...省略部分代码,
//将 webpack.config.js 配置为导出一个函数,根据 mode 参数动态调整配置
module.exports = (env, argv) => {
if (argv.mode === 'development') { config.devtool = 'source-map'; }
if (argv.mode === 'production') { /** .... */ }
return config;
};
方案二:定义不同的 webpack.config.js
方案三:借助 cross-env 设置参数区分环境
cross-env
是一个用于跨平台设置环境变量的工具,特别适用于在 Windows 和 Unix 系统之间进行兼容
通过 cross-env
你可以在命令中设置变量,并在 Webpack 配置中使用这些变量来区分不同的环境:
安装 cross-env
: npm install --save-dev cross-env
配置 package.json
: 在 package.json
中添加脚本,使用 cross-env
设置环境变量
"scripts": {
//....省略部分代码;
"build3": "cross-env NODE_ENV=production webpack --mode=production",
"dev3": "cross-env NODE_ENV=development webpack --mode=development"
},
配置 webpack.config.js
: 将 webpack.config.js
配置中根据 NODE_ENV
环境变量动态调整配置;
//为方便管理引入Node 文件资源管理模块;
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
//WebPack配置:
module.exports = {
//...省略部分代码,
module:{ //加载器:
rules: [ //规则列表:
//优化-提取 css 代码
{
test: /\.css$/i, //匹配所有的 .css 文件
use: [
process.env.NODE_ENV === 'development'?'style-loader':MiniCssExtractPlugin.loader
,"css-loader" ],
},
//打包Less代码
{
test: /\.less$/i, //匹配所有的 .less 文件
use: [process.env.NODE_ENV === 'development'?'style-loader':MiniCssExtractPlugin.loader
,"css-loader","less-loader"],
},
],
},
}
process.env.NODE_ENV === 'development'?'style-loader':MiniCssExtractPlugin.loader
使用:三元运算符,根据命令行参数判断,而切换不同的加载器:
开发模式使用:style-loader
、生产模式使用:MiniCssExtractPlugin.loader
加载器;
DefinePlugin 前端注入环境变量:
🆗,上述我们通过命令行设置环境变量,实现开发\生产环境配置的切换:
但是: cross-env 设置的只支持Node.Js
环境生效,前端的代码无法访问 process.env.NODE_ENV
那么,开发者如何在前端代码中判断开发\生产环境呢😗❓:DefinePlugin插件,支持定义、获取配置中的值;
-
DefinePlugin
是 Webpack 提供的一个插件,用于在:编译时定义全局常量
这些常量可以在代码中使用: 并在编译时被替换为指定的值,并支持表达式赋值;
-
如此在
Webpack.config.js
中定义常量,值:process.env.NODE_ENV
即可轻松判断运行开发、生产环境;
配置 webpack.config.js
//引入webpack包其内置插件: DefinePlugin...
const webpack = require('webpack');
//WebPack配置:
module.exports = {
//插件: 给Webpack提供更多功能;
plugins: [
//...省略部分代码,
//使用DefinePlugin插件定义: 程序全局变量;
new webpack.DefinePlugin({
//全局变量定义规则:
//"字符串":"字符串,因为: 插件底层会直接替换文,所以使用JSON.stringify来确保正确的字符串格式;"
'MODEI': JSON.stringify(process.env.NODE_ENV),
'MODEII': 'process.env.NODE_ENV',
'VERSION': '版本1.0.0',
})
],
}
注意:定义常量值需要使用JSON.stringify
转换,如果需要进行环境判断还需要结合:cross-env参数
启动命令行: cross-env NODE_ENV=production webpack --mode=production
\ --mode=development
功能常用场景:
前端项目中,开发模式下打印语句生效,生产模式下打印语句失效:
//1.webpack 中配置 DefinePlugin 插件
//2.前端代码判断开发\生产环境,给console.log函数对象重新赋值,生产环境则打印语句失效;
if (process.env.NODE_ENV === 'production') {
console.log = function() {}
}
console.log('开发模式下好用,生产模式下失效')
前端项目中,Axios开发环境、生产环境可能使用的请求IP不同,也可以使用其进行管理:
webpack.config.JS: DefinePlugin中定义不同环境的axios请求;
const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = (env, argv) => {
const isDevelopment = process.env.NODE_ENV === 'development';
const isProduction = process.env.NODE_ENV === 'production';
return {
mode: isDevelopment ? 'development' : 'production',
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
'API_BASE_URL': JSON.stringify(isProduction ?
'https://api.example.com' : 'http://localhost:3000'),
}),
],
};
};
请求.JS: axios根据不同的环境,请求不同的IP路径接口;
import axios from 'axios';
const apiClient = axios.create({
baseURL: API_BASE_URL,
headers: {'Content-Type': 'application/json', },
});
Source Map 开发环境调错:
因为:WebPack打包之后,代码被压缩和混淆:如果发送错误无法正确定位源代码位置(行数和列数:
- 经过测试发现: 当程序中存在错误,因为
JS文件
是被压缩管理的,浏览器定位错误位置:24行; - 而实际开发版本的
JS
20行错误: 虽然此处误差还可以接受,如果项目变大那么则非常不方便定位异常;
Source Map 更轻松地调试代码:
Source Map 会将编译后的代码映射回原始源代码: 这样你在调试时可以看到原始代码,而不是编译后的代码;
注意:Source Map 仅适用于开发环境,不要在生产环境使用(防止被轻易查看源码位置)
配置 webpack.config.js
使用Source Map: 可以在 webpack.config.js
中配置 devtool
选项,常见的 devtool
选项有:
- source-map: 生成独立的 Source Map 文件,适合生产环境
- eval-source-map: 每个模块都生成一个内联的 Source Map,构建速度更快
- inline-source-map: 将 Source Map 内联到编译后的文件中,适合开发环境
为了方便管理:WebPack.config.js 不同环境及其配置,可以将将整个配置定义在一个对象中:统一 module.exports=xxx
//为方便管理引入Node 文件资源管理模块;
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
//引入webpack包其内置插件: DefinePlugin...
const webpack = require('webpack');
//1.定义WebPack配置为一个对象 config = { 其中定义配置属性 };
const config = {
//...省略...部分配置代码;
}
//2.可以在其外部通过.xxx形式新增属性,也更方便判断处理;
//开发环境下使用 sourcemap 选项
if (process.env.NODE_ENV === 'development') {
config.devtool = 'inline-source-map'
}
//3.最后将对象统一赋值给 module.exports 暴漏出去;
module.exports = config;
- ①:定义WebPack配置为一个对象
const config = { 其中定义配置属性 }
- ②:可以在其外部通过.xxx形式新增属性,也更方便判断处理;
- ③:最后将对象统一赋值给 module.exports 暴漏出去;
Webpack 设置解析别名路径
设置 Webpack 如何设置路径别名,方便我们引入目标模块:
在 Webpack 中设置解析别名路径可以使代码更简洁,提升可读性和维护性,以下是如何配置别名路径的步骤:
/** indexJS中引入并使用 /utils/checkJS文件暴漏函数 */
//import { checkPhone, checkCode } from '../utils/check.js' 原路径;
import { checkPhone, checkCode } from '@/utils/check.js' //别名+路径;
//对于路径前缀特别长的代码编写过程实在是不方便管理和维护,如果中途需要切换路径使用: 路径别名,可以轻松方便的进行管理;
配置 webpack.config.js
//...省略部分代码...
const config = {
//...省略部分代码...
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'别名': '别名映射的路径地址',
}
}
}
优化-CDN使用
实际开发过程中:我们会使用很多的第三方库: 这导致在打包部署:dist部署文件夹
会非常的大;
如何才能,减少打包的大小呢?——>CDN: 在部署打包时候,三方依赖可以使用CDN进行配置;
什么是CDN: Content Delivery Network
全称:内容分发网络,是一组分布在不同地理位置的服务器群;
- 提高可靠性:多个服务器节点可以实现负载均衡,避免单点故障,提高网站的可用性
- 加速网站访问:通过将内容缓存到离用户最近的节点,减少数据传输的距离和时间
- 减轻源站压力:缓存静态资源,减少对源站的请求次数,降低源站的负载
- 提升用户体验:减少页面加载时间,提高用户的访问速度和满意度
将三方依赖,使用远程私人、公司CDN服务器中访问,就可以极大的减轻本地的包大小,减轻服务器运行压力;
优化需求:
生产环境的第三方依赖使用CND进行管理,减轻服务器内存
开发环境因为是本地所以: 还是建议NPM使用本地的包,**实际情况根据公司而定,部分公司其实用不上这个😓
本地环境使用三方依赖:
广告: AXIOS学习📄🔗
NPM安装axios 依赖: npm install axios --save
使用三方axios包,登录页面打开查询北京的天气情况: 此处接口来源,中国气象局公共API;
import axios from 'axios'
/** 省略部分代码 */
/** 测试使用axios 查询南京的天气: */
axios.get('http://www.nmc.cn/rest/weather?stationid=CxOWZ&_=1721313627036')
.then(response => {
console.log(response.data);
console.log(response.data.data.real.warn.alert);
})
.catch(error => {
console.error(error);
});
生产环境打包,CDN管理三方依赖:
BootCDN - Bootstrap 中文网开源项目免费 CDN 加速服务
如何根据生产环境,而使用CND依赖呢: webpack.config.js
中 externals外部扩展选项,防止某些 import 的包被打包)
//为方便管理引入Node 文件资源管理模块...省略
//WebPack配置:
const config = {
//...省略...部分代码
//入口 entry: path.resolve(__dirname, 'src/login/index.js'),
entry: {
'login': path.resolve(__dirname, 'src/login/index.js')
},
plugins: [ //插件: 给Webpack提供更多功能;
new HtmlWebpackPlugin({
template: path.resolve(__dirname,'public/login.html'), // 模板文件
filename: path.resolve(__dirname,'dist/login/login.html'), // 输出文件
//自定义属性,在html模板中 <%=htmlWebpackPlugin.options.useCdn%> 判断是否访问使用;
useCdn: process.env.NODE_ENV === 'production'
//指定引入打包后的JS模块和 entry 的 key 匹配
chunks: ['login']
}),
],
}
// 开发环境下使用 sourcemap 选项...省略
// 生产环境下使用相关配置
if (process.env.NODE_ENV === 'production') {
// 外部扩展(让 webpack 防止 import 的包被打包进来)
config.externals = {
// key:import from 语句后面的字符串
// value:留在原地的全局变量(最好和 cdn 在全局暴露的变量一致
'axios': 'axios'
}
}
module.exports = config;
在html模板中,通过自定义属性判断是否使用CND资源:
并通过在webpack.config.js
中配置管理了,html模板对应使用的.JS文件
;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- <link href="../src/login/login.css" rel="stylesheet"> -->
<title>数据管理平台</title>
</head>
<body>
<div class="login-wrap">
<div class="title">头条</div>
<div>
<form class="login-form">
<div class="item">
<input type="text" class="form-control" name="mobile" placeholder="请输入手机号" value="">
</div>
<div class="item">
<input type="text" class="form-control" name="code" placeholder="默认验证码" value="">
</div>
<div class="item">
<button type="button" class="btn btn-primary btn">登 录</button>
</div>
</form>
</div>
</div>
<% if(htmlWebpackPlugin.options.useCdn){ %>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.6/axios.min.js"></script>
<% } %>
<!-- 个人觉得好鸡肋,还需要事先定义好JS引用位置; -->
<!-- <script src="../src/login/index.js"></script> -->
<!-- webpack.config.js plugins new HtmlWebpackPlugin({ chunks: 指定引入文件对应JS }) -->
</body>
</html>
启动命令: cross-env NODE_ENV=production webpack --mode=production
🆗,到此就完成了,查看dist
中代码: 可以清楚的看到体积变小了,且页面上Axios也是CND引用 还不影响最终效果
WebPack打包多页面:
Webpack打包多页面应用是一种常见的需求,特别是在需要处理多个独立页面的项目中:
-
多页面打包的核心是配置多个入口
entry
和多个HTML模板HtmlWebpackPlugin
-
每个页面可以看作是一个独立的单页应用,Webpack会为每个页面生成独立的打包文件;
目录结构:
假设你的项目目录结构如下:
src/
├── pages/
│ ├── page1/
│ │ ├── app.js
│ │ ├── index.html
│ ├── page2/
│ │ ├── app.js
│ │ ├── index.html
配置 webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
page1: './src/pages/page1/app.js',
page2: './src/pages/page2/app.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name]/[name]-bundle.js',
},
plugins: [
new HtmlWebpackPlugin({
template: './src/pages/page1/index.html',
filename: 'page1.html',
chunks: ['page1'],
}),
new HtmlWebpackPlugin({
template: './src/pages/page2/index.html',
filename: 'page2.html',
chunks: ['page2'],
}),
],
};
-
多个入口:在
entry
中配置多个入口,每个入口对应一个页面的主文件 -
输出文件名:使用
[name]
占位符生成不同页面的打包文件, -
多个HtmlWebpackPlugin实例:
为每个页面配置一个HtmlWebpackPlugin实例,指定模板文件和包含的
chunks
chunks: 指定引入打包后的JS模块和 entry 的 key 匹配
优化-分割公共代码:
splitChunks
是 Webpack 提供的一个强大的功能,用于优化代码打包;
它的主要目的是将重复的模块代码分离到单独的文件中,以减少重复打包的内容,从而提高加载性能:
- 随着应用程序规模的增长,JavaScript 文件的大小也会增加,
- 一个大的 JavaScript 文件会导致页面加载时间过长,影响用户体验
- 通过合理的拆包策略,可以将一个大的 JavaScript 文件分割成多个较小的代码块,将公用的代码抽离成单独的 chunk
splitChunks
的配置项:
-
chunks:指定哪些 chunk 需要进行拆分,常用的取值有:
async
默认值,只对异步加载的模块进行拆分)、initial
对入口模块进行拆分) 、all
对所有模块进行拆分) -
minSize:生成 chunk 的最小体积,单位为字节bytes),内容超过这个值才会进行拆分;
-
minChunks:在拆分之前,必须共享的模块的最小 chunk 数
-
maxAsyncRequests:按需加载时,允许的最大并行请求数
-
maxInitialRequests:入口点允许的最大并行请求数
-
cacheGroups:通过
cacheGroups
自定义chunk
分组,设置test
对模块进行过滤,符合条件的模块分配到相同的组
module.exports = {
optimization: {
splitChunks: {
chunks: 'all', // 对所有的chunk进行拆分
minSize: 20000, // 拆分 chunk 的最小体积
minChunks: 1, // 需在一个模块中共享才进行拆分
maxAsyncRequests: 30, // 按需加载时的最大并行请求数
maxInitialRequests: 30, // 入口点的最大并行请求数
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
};
优化策略:
通过合理的拆包策略,可以显著提升应用程序的性能,代码分离通过将代码按需加载,减小初始下载量;
而打包分离将应用程序拆分成多个块,实现增量更新,减少不必要的下载;
路过的大佬,请多多关注: 代码已经Git管理: