Webpack
一、什么是 Webpack
是一个用于现代 JavaScript 应用程序的静态模块打包工具
静态模块:html、css、js、图片等固定内容的文件
打包:将静态模块的内容,压缩,整合,转译等(前端工程化)
- 将 less/sass 转成 css 代码
- 把 ES6+ 降级成 ES5
- 支持多种模块标准语法
二、使用 Webpack
安装:
//保存到开发目录
npm i webpack webpack-cli --save-dev
设置自定义打包命令:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mDTPuj5f-1689042707139)(C:\Users\mzhj\AppData\Roaming\Typora\typora-user-images\image-20230705221304210.png)]
使用打包命令:
npm run <自定义打包命令>
修改 Webpack 打包入口和出口
- 在根目录下新建 webpack.config.js 文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lxlgQdS5-1689042707140)(C:\Users\mzhj\AppData\Roaming\Typora\typora-user-images\image-20230705222749746.png)]
- 修改:
const path = require('path');
module.exports = {
//entry 为入口地址
entry: path.resolve(__dirname, 'src/login/index.js'),
output: {
//出口地址
path: path.resolve(__dirname, 'dist'),
//出口文件名
filename: './login/index.js',
clean: true //生成打包内容之前,清空输出目录
},
};
自动生成 html 文件:
使用插件 html-webpack-plugin:在Webpack 打包时生成 html 文件
安装插件(到开发环境)
npm i --save-dev http-webpack-plugin
在 webpack 配置文件中使用插件
const path = require('path');
//引用插件
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: path.resolve(__dirname, 'src/login/index.js'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: './login/index.js',
clean: true //生成打包内容之前,清空输出目录
},
//插件 生成 html 文件
plugins: [new HtmlWebpackPlugin({
//模板文件
template: path.resolve(__dirname, 'public/login.html'),
//输出文件
filename: path.resolve(__dirname, 'dist/login/index.html')
})]
};
打包 css 代码:
webpack 默认只能识别 js 代码
使用加载器 css-loader:解析 css 代码
使用加载器 style-loader:将解析后的 css 代码插入到 DOM
npm i css-loader
npm i style-loader
在 webpack 配置中使用加载器
module.exports = {
//加载器 让 webpack 识别更多内容
module: {
rules: [
{
test: /\.css$/i,//解析 css 文件
use: ["style-loader", "css-loader"],//从后往前运行
},
],
},
};
优化-提取 CSS 代码
好处:
css 文件可以被浏览器缓存,减少 js 文件体积
插件 mini-css-extract-plugin:提取 css 代码
-
不能与 style-loader 一起使用
-
建议与 css-loader 一起使用
安装:
npm install --save-dev mini-css-extract-plugin
在 webpack 配置中使用:
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
//生成 css 文件
plugin: [new MiniCssExtractPlugin()]
//加载器 让 webpack 识别更多内容
module: {
rules: [
{
test: /\.css$/i,//解析 css 文件
use: [ MiniCssExtractPlugin.loader, "css-loader" ],//从后往前运行
},
],
},
};
优化-压缩 css
使用:css-minimizer-webpack-plugin 插件
安装:
npm i css-minimizer-webpack-plugin --save-dev
在 webpack 配置中使用:
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
module.exports = {
module: {
rules: [
{
test: /.s?css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
//自定义压缩功能
optimization: {
minimizer: [
'...',//保留压缩 js 文件的功能
new CssMinimizerPlugin(),//压缩 css 的功能
],
minimize: true //允许在开发环境中使用
},
plugins: [new MiniCssExtractPlugin()],
};
打包 less 代码:
加载器 less-loader:把 less 代码编译成 css 代码
安装:
//安装 less-loader 需要安装 less
npm i less less-loader --save-dev
使用:
module: {
rules: [
{//提取 css 代码
test: /\.css$/i,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
],//从后往前运行
},
{//提取 less 代码
test: /\.less$/i,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"less-loader"
],//从后往前运行
},
],
},
打包图片:
资源模块: webpack5 内置模块(字体,图片等)打包,无需额外 loader
配置:
module: {
rules: [
{//提取 css 代码
test: /\.(png|jpg|jpeg|gif)$/i,
type: 'asset',//通用 大于 8kb 为 resource 小于为 inline
generator: {
//默认以 output 为基路径
filename: 'assets/[hash][ext][query]'
//占位符 [hash] 得到映射的数字字母组合的字符串
//占位符 [ext] 使用当前模块原本的占位符:.png/.jpg等
//占位符 [query] 保留引入文件时的查询参数(只有URL生效)
}
},
],
},
inline:导出一个data URI (base64 字符串)压入 js 文件
resource:发送一个单独的文件并导出 URL 地址
三、搭建开发环境
配置 webpack-dev-server 快速开发应用程序
作用:启动 Web 服务,自动检测代码变化,热更新到网页
**注意:**dist 目录和打包内容是保存在内存中的(更新快)
在 webpack 配置文件中设置为开发模式
module.exports = {
mode: 'development'
}
开发工具:
通过 npm run watch 启动,他会一直监听代码的变化并修改,但是浏览器的效果需要刷新
在 package.json 文件中添加
"watch": "webpack --watch",
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sNAOXmyw-1689042707141)(C:\Users\mzhj\AppData\Roaming\Typora\typora-user-images\image-20230706135445276.png)]
提供了一个基本的 web server,并且具有 live reloading(实时重新加载) 功能
安装:
npm install --save-dev webpack-dev-server
在 package.json 中配置自定义命令:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "webpack serve --open"
},
注意:
-
webpack-dev-server 借助 http 模块创建 8080 默认 web 服务
-
默认以 public 文件夹作为服务器根目录
-
webpack-dev-server 根据配置,打包相关代码在内存中,作为服务器根目录,以 output.path 的值作为服务器根目录(所以可以直接自己拼接访问 dist 目录下的内容)
-
webpack-dev-middleware
webpack-dev-middleware
是一个封装器(wrapper),它可以把 webpack 处理过的文件发送到一个 server。webpack-dev-server
在内部使用了它,然而它也可以作为一个单独的 package 来使用,以便根据需求进行更多自定义设置。
四、打包模式
告知 webpack 使用相应模式的内置优化
模式名称 | 模式名字 | 特点 | 场景 |
---|---|---|---|
开发模式 | development | 调试代码、实时加载、模块热替换等 | 本地开发 |
生产模式 | production | 压缩代码、资源优化、更轻量等 | 打包上线 |
设置方式
- 在 webpack.config.js 配置文件设置 mode 选项
- 在 package.json 命令行设置 mode 参数
module.exports = {
mode: 'development/production'
}
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --mode=production",
"dev": "webpack serve --open --mode=development"
},
命令行设置的优先级高于配置文件,推荐命令行设置。
打包模式的应用
在开发模式下用 style-loader 内嵌更快,生产模式下提取 css
方法1
webpack.config.js 配置导出函数,局限性大(只接受两种模式)
方法2
借助 cross-env (跨平台通用)包命令,设置参数区分环境
- 下载 cross-env 软件包到当前项目
- 配置自定义命令,传入参数名和值(会绑定到 proccess.env 对象下)
- 在 webpack.config.js 区分不同环境使用不同配置
下载:
npm i cross-env --save-dev
在 package.json 中配置自定义命令
"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"
},
在 webpack.config.js 中区分不同环境使用不同配置
module: {
rules: [
{
test: /\.css$/i,
use: [
process.env.NODE_ENV === 'development' ? 'style-loader' : MiniCssExtractPlugin.loader,
"css-loader",
],//从后往前运行
},
{
test: /\.less$/i,
use: [
process.env.NODE_ENV === 'development' ? 'style-loader' : MiniCssExtractPlugin.loader,
"css-loader",
"less-loader"
],//从后往前运行
},
]
}
方法三
前端-注入环境变量
使用 webpack 内置的 DefinePlugin 插件
在编译时,将前端代码中匹配的变量名,替换为值或表达式
const webpack = require('webpack')
moudle.exports = {
plugins: [
new HtmlWebpackPlugin({
new webpack.DefinePlugin({
'process.env.NODE_ENV' : JSON.stringify( process.env.NODE_ENV )
})
],
}
js 文件中使用
if (process.env.NODE_ENV === 'production') {
console.log = function() {}
}
console.log('开发模式下好用,生产模式下失效')
五、开发环境调错 - source map
代码被压缩混淆,无法正确定位源代码位置(行数和列数)
source map:可以准确追踪 error 和 waring 在原始代码的位置
设置:
webpack.config.js 配置 devtool 选项
module.exports = {
devtool: 'inline-source-map'
}
inline-source-map : 原始代码的信息一起打包在 js 文件内
仅在开发环境中使用,不在生产环境使用,防止被轻易查看源码位置
六、解析别名 alias
配置模块如何解析,创建 import 引入路径别名,来确保模块引入变得更简单
在 webpack.config.js 中配置解析别名 @ 来代表 src 绝对路径
const config = {
//...
resolve: {
alias: {
'@' : path.resolve(__diename, 'src')
}
}
}
module.exports = config
七、优化
CDN 的使用
CDN 内容分发网络,指的是一组分布在各个地区的服务器集合
把静态资源/第三方库放在 CDN 网络中各个服务器上,供用户就近访问
好处:
减轻自己服务器的请求压力,就近请求物理延迟低,配套缓存策略
开发模式使用本地第三方库,生产模式使用 CDN 加载引入
在 html 中引用第三方库的 CDN 地址并用模板语法判断
<head>
//根据开发模式决定是否采用 cdn
<% if(htmlWebpackPlugin.options.useCdn) {%>
<link href="https://cdn.bootcdn.net/xxx/xxx.css" rel="stylesheet">
<% } %>
</head>
//根据开发模式决定是否采用 cdn
<% if(WebpackHttp) {%>
<script src="https://cdn.bootcdn.net/xxx/xxx/xxx/xxx.css"></script>
<% } %>
配置 webpack.config.js 中 externals 外部扩展选项(防止某些 import 的包被打包)
const config = {
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 引入
}),
],
}
//生产环境下使用相关的配置
if (process.env.NODE_ENV === 'production') {
//外部扩展 防止打包
config.externals = {
//key: import from 语句后面的字符串
//value:留在原地的全局变量(最好和 cdn 在全局暴露的变量一致)
'bootstrap/dist/css/bootstrap.min.css': 'bootstrap',
'axios' : 'axios'
}
}
module.exports = config
分割公共代码
将公共代码提取
- 配置 webpack.config.js 的 splitChunks 分割功能
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 目录下位置
}
}
}
}
八、多页面打包
单页面:单个 html 文件,切换 DOM 的方式实现不同业务逻辑展示
多页面:多个 html 文件,切换页面实现不同业务逻辑展示
添加多页面打包入口
entry:
{
'login': path.resolve(__dirname, 'src/login/index.js'),
'content': path.resolve(__dirname, 'src/content/index.js'),
'publish': path.resolve(__dirname, 'src/publish/index.js')
},
出口使用占位符动态生成
output: {
path: path.resolve(__dirname, 'dist'),
filename: './[name]/index.js',
clean: true //生成打包内容之前,清空输出目录
}
依据不同模块进行 html 打包
//导入 webpack 内置模块
const { Chunk } = require('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'], //引入 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'], //引入 content 模块 (和 entry 的 key 一致)
}),
new HtmlWebpackPlugin({
//模板文件
template: path.resolve(__dirname, 'public/publish.html'),
//输出文件
filename: path.resolve(__dirname, 'dist/publish/index.html'),
useCdn: process.env.NODE_ENV === 'production', //生产模式下使用 cdn 引入
chunks: ['publish'], //引入 content 模块 (和 entry 的 key 一致)
}),
]
生产环境取消打包的外部扩展
//生产环境下使用相关的配置
if (process.env.NODE_ENV === 'production') {
//外部扩展 防止打包
config.externals = {
//key: import from 语句后面的字符串
//value:留在原地的全局变量(最好和 cdn 在全局暴露的变量一致)
'bootstrap/dist/css/bootstrap.min.css': 'bootstrap',
'axios' : 'axios',
'form-serialize' : 'serialize',
'@wangeditor/editor' : 'wangEditor'
}
}
public/publish.html’),
//输出文件
filename: path.resolve(__dirname, ‘dist/publish/index.html’),
useCdn: process.env.NODE_ENV === ‘production’, //生产模式下使用 cdn 引入
chunks: [‘publish’], //引入 content 模块 (和 entry 的 key 一致)
}),
]
### 生产环境取消打包的外部扩展
//生产环境下使用相关的配置
if (process.env.NODE_ENV === ‘production’) {
//外部扩展 防止打包
config.externals = {
//key: import from 语句后面的字符串
//value:留在原地的全局变量(最好和 cdn 在全局暴露的变量一致)
‘bootstrap/dist/css/bootstrap.min.css’: ‘bootstrap’,
‘axios’ : ‘axios’,
‘form-serialize’ : ‘serialize’,
‘@wangeditor/editor’ : ‘wangEditor’
}
}