打包模式
打包模式 是为了告知 webpack 使用相应模式的内置优化。
模式分类
模式名称 | 模式名字 | 特点 | 场景 |
---|---|---|---|
开发模式 | development | 调试代码,实时加载,模块热替换等 | 本地开发 |
生产模式 | production | 压缩代码,资源优化,更轻量等 | 打包上线 |
设置方式
方式1: 在webpack.config.js配置文件设置mode选项
module.exports = {
// 打包模式 (development 开发模式-使用相关内置优化)
mode:'development',
}
方式2: 在package.json命令行设置mode参数
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --mode=production", // 生产模式
"dev": "webpack serve --open --mode=development" // 开发模式
},
注意:命令行设置的优先级高于配置文件中的
打包模式的应用
需求
在开发模式下:使用 style-loader 内嵌 css 代码在 js 中,让热替换更快
载生产模式下:提取 css 代码,让浏览器缓存和并行下载 js 和 css 文件
步骤
- 下载 cross-env 软件包到当前项目中
命令:npm i cross-env --save-dev - 配置自定义命令,传入参数名和值到 process.env 对象上( process.env是 Node.js 环境变量)
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "cross-env NODE_ENV=production webpack --mode=production",
"dev": "cross-env NODE_ENV=development webpack serve --open --mode=development"
},
注意:NODE_ENV=production 和 webpack打包时的–mode=production 是没有关系的,二者互不影响
- 在 webpack.config.js 调用使用做判断区分
// 打包css代码
{
test: /\.css$/i,
use: [process.env.NODE_ENV ==='development' ? 'style-loader': MiniCssExtractPlugin.loader, 'css-loader'],
},
需求
前端项目中,在开发模式下打印语句生效,在生产模式下打印语句失效
问题: cross-env 设置的只在 Node.js 环境生效,前端代码无法访问 process.env.NODE_ENV
解决方法: 使用Webpack内置的DefinePlugin插件
作用: 在编译时,将前端代码中匹配的变量名,替换为值或表达式
步骤
- 在webpack 中配置 DefinePlugin 插件
const webpack = require('webpack')
module.exports = {
plugins: [
new webpack.DefinePlugin({
// key是注入到打包后的前端JS代码中作为全局变量
// value是变量对应的值(在corss-env 注入在 node.js 中的环境变量字符串)
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
})
],
}
- 在前端代码中使用,并在两个模式下观察区别
if(process.env.NODE_ENV === 'production'){
console.log = function(){}
}
console.log('开发模式下生效, 生产模式下失效')
开发环境调错 —source map
问题: 代码被压缩和混淆,无法正确定位源代码位置(行数和列数)
source map: 可以准确追踪error和warning在原始代码的位置
设置: 在webpack.config.js配置devtool选项
inline-source-map选项:把源码的位置信息一起打包在js 文件内
const config = {
// 其他代码...
};
// 开发环境下使用source map选项
if(process.env.NODE_ENV === 'development'){
config.devtool = 'inline-source-map'
}
module.exports = config
注意: source map仅适用于开发环境,不要在生产环境使用(防止被轻易查看源码位置)
解析别名alias
解析别名: 路径解析别名设置,让前端代码引入路径更简单(而且使用绝度路径)
解决: 在webpack.config.js 中配置解析别名来代表 src 绝对路径
const config = {
// 解析别名
resolve: {
alias: {
// __dirname 会动态获取当前文件所在文件夹的绝对路径
// @ 表示 src 文件夹
'@': path.resolve(__dirname, 'src')
}
}
}
在前端代码中引入路径
import yourAxios from '@/utils/request.js'
console.log(yourAxios)
优化-CDN使用
CDN定义: 内容分发网络,指的是一组分布在各个地区的服务器
作用: 把静态资源文件/第三方库放在CDN网络中各个服务器中,供用户就近请求获取
好处: 减轻自己服务器请求压力,就近请求物理延迟低,配套缓存策略
需求: 开发模式使用本地第三方库,生产模式下使用CDN加载引入
步骤:
- 在 html 中引入第三方库的 CDN 地址并用模板语法判断
<% if(htmlWebpackPlugin.options.useCdn){ %>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.2.3/css/bootstrap.min.css" rel="stylesheet">
<% } %>
<% if(htmlWebpackPlugin.options.useCdn){ %>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.6/axios.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.2.3/js/bootstrap.min.js"></script>
<% } %>
- 配置 webpack.config.js 中 externals 外部扩展选项(防止某些 import 的包被打包)
// 生产环境下使用相关配置
if (process.env.NODE_ENV === 'production') {
// 外部扩展(让 webpack 防止 import 的包被打包进来)
config.externals = {
// key:import from 语句后面的字符串
// value:留在原地的全局变量(最好和 cdn 在全局暴露的变量一致)
'bootstrap/dist/css/bootstrap.min.css': 'bootstrap',
'axios': 'axios',
}
}
const config = {
// 插件(给Webpack提供更多功能)
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'public/login.html'), // 模版文件
filename: path.resolve(__dirname, 'dist/login/index.html'), // 输出文件
useCdn: process.env.NODE_ENV === 'production'
}),
}
多页面打包
- 准备源码(html,css,js)放入相应位置,并改用模块化语法导出
- 配置 webpack.config.js 多入口和多页面的设置
const config = {
// 入口
entry: {
'login': path.resolve(__dirname, 'src/login/index.js'),
'content': path.resolve(__dirname, 'src/content/index.js'),
},
// 出口
output: {
path: path.resolve(__dirname, 'dist'),
filename: './[name]/index.js',
clean: true //生成打包后内容之前,清空输出目录
},
// 插件(给Webpack提供更多功能)
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'public/login.html'), // 模版文件
filename: path.resolve(__dirname, 'dist/login/index.html'), // 输出文件
useCdn: process.env.NODE_ENV === 'production', // 生产模式下使用 cdn 引入的地址
chunks: ['login'] // 引入哪些打包后的模块(和 entry 中的 key 一致)
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'public/content.html'), // 模版文件
filename: path.resolve(__dirname, 'dist/content/index.html'), // 输出文件
useCdn: process.env.NODE_ENV === 'production', // 生产模式下使用 cdn 引入的地址
chunks: ['content'] // 引入哪些打包后的模块(和 entry 中的 key 一致)
}),
new MiniCssExtractPlugin({
filename: './[name]/index.css'
}), // 生成css文件
]
}
优化-分割公共代码
需求: 把2个以上页面引用的公共代码提取
步骤:
- 配置webpack.config.js的splitChunks分割功能
const config = {
optimization: {
// 代码分割
splitChunks: {
chunks: 'all', // 所有模块动态非动态移入的都分割分析
cacheGroups: { // 分隔组
commons: { // 抽取公共模块
minSize: 0, // 抽取的chunk最小大小字节
minChunks: 2, // 最小引用数
reuseExistingChunk: true, // 当前 chunk 包含已从主 bundle 中拆分出的模块,则它将被重用
name(module, chunks, cacheGroupKey) { // 分离出模块文件名
const allChunksNames = chunks.map((item) => item.name).join('~') // 模块名1~模块名2
return `./js/${allChunksNames}` // 输出到 dist 目录下位置
}
}
}
}
},
}