本节目标
- 基础配置
基础配置
webpack是一个JS的静态打包工具
静态模块: 编写代码过程中的, html, css, js, 图片等固定内容的文件
打包: 把静态模块的内容, 压缩, 整合, 转译等 (前端工程化)
转译: 把less/sass转成css代码, 把ESC6+ 降级成ES5, 支持多种模块标准语法
体验
- 新建并初始化项目, 编写业务源代码
npm init -y
- 下载webpack webpack-cli 到当前目录中(版本独立), 并配置局部自定义命令
npm i webpack webpack-cli --save-dev
- 运行打包命令, 自动产生dist分发文件夹(压缩和优化后, 用于最终运行的代码)
npm run build
- 执行过程
入口和出口
通过webpack配置, 影响打包的过程和结果
步骤
-
项目根目录, 新建webpack.config.js配置文件
-
导出配置对象, 配置入口/出口文件的路径
-
只有和入口产生直接/间接的引入关系, 才会被打包
const path = require('path');
module.exports = {
// 自定入口文件
entry: path.resolve(__dirname, 'src/login/index.js'),
// 自定输出文件
output: {
// 文件位置
path: path.resolve(__dirname, 'dist'),
// 文件名称
filename: './login/inde.js',
},
};
手动引入HTML
需求**:**点击登录按钮, 判断手机号和验证码程度
步骤
- 准备登录页面(index.html)
- 编写核心JS逻辑代码(index.js), 打包
- 手动复制网页到dist中,
- 在页面中引入打包后的js, 运行
自动生成HTML
HtmlWebpackPlugin简化了HTML文件的创建, 以便为你的webpack包提供服务
步骤
-
位置: 官网搜索HtmlWebpackPlugin
-
安装: npm install --save-dev html-webpack-plugin
-
配置
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');module.exports = {
......
// 注册插件
plugins: [new HtmlWebpackPlugin({
// 插件配置
template: path.resolve(__dirname, 'public/login.html'), // 模板文件
filename: path.resolve(__dirname, 'dist/login.html'), // 输出文件
})],
};
打包css代码
webpack默认只识别js代码, 通过配置loader加载器进行解析
使用
-
位置: 官网搜索css-loader
-
css-loader( 解析css ) 和 style-loader( 把解析后的css插入到DOM ) 配合使用
-
安装 npm install css-loader style-loader --save-dev
-
引入css文件到src/login/index.js中
-
配置webpack.config.js 让其拥有该加载器的功能
module.exports = {
... ...
// 配置加载器
module: {
rules: [
{
test: /.css$/i,
use: ['style-loader', 'css-loader'],
},
],
},
}; -
安装 npm i bootstrap
-
引入 import 'bootstrap/dist/css/bootstrap.min.css'
提取css代码
把css代码提出成单独文件, 可以减少js文件体积, 并且css文件可以被浏览器缓存
步骤
-
位置: 官网搜索MiniCssExtractPlugin
-
使用mini-css-extract-plugin插件提取css
-
下载命令: npm install --save-dev mini-css-extract-plugin
-
配置文件: 在webpack.config.js中, 配置使用
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
plugins: [new MiniCssExtractPlugin()],
module: {
rules: [
{
test: /.css$/i,
// css与js一体打包
// use: ['style-loader', 'css-loader'], // 被注释的加载器
// 提取css插件
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
}; -
注意: 该插件不能和style-loader一起使用
压缩css代码
css代码提取后没有被压缩, 通过css-minimizer-webpack-pligin插件, 压缩自己的css文件
步骤
-
位置: 官网搜索CssMinimizerWebpackPlugin
-
安装: npm install css-minimizer-webpack-plugin --save-dev
-
配置
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
module.exports = {
... ...
// 优化配置
optimization: {
// 最小化
minimizer: [
// 在 webpack@5 中,你可以使用...
语法来扩展现有的 minimizer(即terser-webpack-plugin
),将下一行取消注释
// 保证js代码还能压缩
...
,
new CssMinimizerPlugin(),
],
},
};
打包less代码
使用less-loder加载器, 把less代码转译为css代码
步骤
-
位置: 官网搜索less-loader
-
新建 index.less代码文件, 设置背景图, 引入代码文件
-
下载: npm install less less-loader --save-dev
-
注意 less-loader 需要配置less软件包使用
-
配置
module.exports = {
module: {
rules: [
// 提取css的配置
... ...
// less打包配置
{
test: /.less$/i,
use: [
// compiles Less to CSS
// MiniCssExtractPlugin.loader 与 style-loader 只能用一个
// 'style-loader',
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
],
},
],
},
};
打包图片
webpack5内置资源模块(字体,图片等)打包模块, 无需额外的loader, 只需要一些配置即可
module.exports = {
// 配置加载器(识别更多模块文件的内容)
module: {
rules: [
// 提取css的配置
... ...
// less打包配置
... ...
// 图片文件打包配置
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset',
generator: {
// 输出位置
filename: 'assets/[hash][ext][query]',
},
}
],
},
... ...
};
/**
* JS中
* 引入本地图片资源 要用import方法
* 如果是网络图片, 可以使用字符串网址
*/
import logo from './assets/logo.png'
const thenImg = document.createElement('img')
thenImg.src = logo
document.querySelector('.login-wrap').appendChild(thenImg)
配置解释
- 位置: 官网搜索 '资源模块'
- test: 配置文件类型( 根据后缀名匹配文件 )
- type: 配置打包类型( asset表示自动适配 )
- 自动适配的依据是文件大小, 判断临界默认值为8kb
- 大于8kb的文件: 发送一个单独的文件并导出URL地址( 拷贝图片 )
- 小于8kb的文件: 导出一个data URL(base64字符串)
- filename: 配置打包的出口( 打包后文件存放位置 )
- 占位符 [hash] 对模块内容做算法计算, 得到映射的数字字母组合的字符串( 生成文件名 )
- 占位符 [ext] 使用当前模块原本的占位符, 例如 .png .jpg等字符串 ( 沿用文件后缀 )
- 占位符 [query] 保留引入文件时代码中查询参数( 只有URL下有效 ) ( 不太常用 )
登录页
完成登录页的核心流程, 以及Alert警告框的使用
流程
步骤
- 下载使用axios (npm i axios)
- 准备并修改utils工具包下的文件 (导出函数)
- 编写逻辑代码, 打包后运行
代码
/**
* 完成登录功能
* 1,下载axios
* 2,修改utils下的文件, 导出函数
* 3,完成登录, 打包运行
*/
import axios from '../utils/request'
import { myAlert } from '../utils/alert'
document.querySelector('.btn').addEventListener('click', function () {
const p = document.querySelector('.login-form [name=mobile]').value
const c = document.querySelector('.login-form [name=code]').value
if (!phone(p)) {
// alert('手机号不正确')
myAlert(false, '手机号不正确')
return
}
if (!code(c)) {
// alert('验证码不正确')
myAlert(false, '验证码不正确')
return
}
axios({
url: '/v1_0/authorizations',
method: 'POST',
data: {
mobile: p,
code: c
}
}).then(() => {
myAlert(true, '登录成功')
}, (error) => {
myAlert(false, error.response.data.message)
})
})
开发环境
通过webpack-dev-server插件, 实现代码热更新
介绍
- 作用 启动Web服务器, 自动检测代码变化, 热更新到网页
- 注意 dist目录和打包内容是在内存中(更新快)
- 位置 官网搜索 webpack-dev-server
- 原理 使用http模块创建 8080 web服务
- 默认以 public 文件夹作为服务器根目录
- 根据配置, 打包相关代码在内存中(更新快),以output.path的值作为服务器根目录
- 所以可以直接自己拼接访问 dist 目录下的内容
步骤
-
下载 npm install --save-dev webpack webpack-dev-server
-
设置 设置默认为开发模式
module.exports = {
// 配置打包模式(使用相关内置优化)
mode: 'development',
... ...
}; -
配置 配置自定义命令
"scripts": {
... ...
"dev": "webpack server --open"
}, -
启动 使用自定义命令启动开发服务器,体验代码热更新
打包模式
告诉webpack使用相应模式的内置优化
设置方式
-
在webpack.config.js配置文件中设置mode选项
-
在package.json命令行设置mode参数 (推荐)
-
命令行设置的优先级高于配置文件的
"scripts": {
"build": "webpack --mode=production",
"dev": "webpack server --open --mode=development"
},
条件打包
在开发模式下应用 style-loader 内嵌css(更快), 在生产模式下提取css代码(并行下载)
方法1
-
webpack.config.js配置导出函数
-
具体代码参考官网, 搜索模式(Mode)
-
缺点: 局限性大, 只能区分2中模式(开发/生产)
方法2
-
借助cross-env(跨平台通用) 包命令, 设置参数区分环境
-
安装 npm i cross-env --save-dev
-
配置自定义命令, 传入参数名和值 ( 会绑定到process.env对象中 ) (Node.js环境变量)
"scripts": {
"build": "cross-env NODE_ENV=production ... ",
"dev": "cross-env NODE_ENV=development ..."
}, -
在webpack.config.js 区分不同环境, 使用不同配置
module.exports = {
module: {
rules: [
// 提取css的配置
{
test: /.css/i, // 提取css插件 // 根据不同环境, 使用不同的插件 use: [ process.env.NODE_ENV === 'production' ? MiniCssExtractPlugin.loader : 'style-loader', "css-loader" ], }, // less打包配置 { test: /\.less/i,
use: [
// 根据不同环境, 使用不同的loader
process.env.NODE_ENV === 'production' ? MiniCssExtractPlugin.loader : 'style-loader',
'css-loader',
'less-loader',
],
},
],
},
}; -
重新打包
-
缺点: 如果不同模式的配置差异很大, 判断和配置起来就会比较麻烦
方法3
- 配置不同的webpcak.config.js
- 适合多种模式差异性较大的情况
注入环境变量
cross-env 设置的环境变量只在Node,js环境生效, 前端代码无法访问
解决
-
使用webpack内置的 DefinePlugin插件
-
位置 官网搜索 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(资源地图) : 可以准确追踪error和warning在原始代码位置
-
inline-source-map选项: 把源代码的位置信息一起打包在JS文件内
-
其他选项: 官网搜索 devtool 查看
-
注意: 该配置仅适用于开发环境, 不要在生产环境使用(防止被轻易查看源码位置)
const config = {...}
if (process.env.NODE_ENV === 'development') {
// 在开发环境中 配置sourcemap选项
config.devtool = 'inline-source-map'
}module.exports = config
/**
- 问题
- error和 warning 的提示信息和实际错误位置不一致, 不方便调试
- 解决
- 启动 webpack 的 cource-map 资源地图功能, 获得准确的错误提示
*/
consolee.warning('222');
解析别名
配置 import 引入路径的别名, 让引入资源更简单(而且使用绝对路径)
在webpack.config.js中 配置解析别名 @ 来代表 src 绝对路径
const config = {
... ...
// 解析
resolve: {
// 配置别名
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
};
module.exports = config
CDN优化
内容分发网络, 就是一组分布在各个地区的服务器. 把静态资源文件/第三方库放在CDN网络中的各个服务器中, 供用户就近请求获取, 减轻自己服务器请求压力, 就近请求物理延迟低, 配套缓存策略
步骤
-
需求: 开发模式使用本地的第三方库, 生产模式下使用CDN加载引入
-
在html中引入 第三方库 的CDN地址 (使用模版语法区分环境)
<head> ... ... <% if(htmlWebpackPlugin.options.useCdn){ %> <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.3/css/bootstrap.min.css" rel="stylesheet"> <% } %> </head> <body> ... ... <% if(htmlWebpackPlugin.options.useCdn){ %> <script src="https://cdn.bootcdn.net/ajax/libs/axios/1.6.8/axios.min.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.3/js/bootstrap.min.js"></script> <% } %> </body> -
在 webpack 中配置 externals 外部扩展选项
const config = {
plugins: [
new HtmlWebpackPlugin({
... ...
// 自定义属性, 在html模版中 <%=htmlwebpackPlugin.options.useCdn%> 访问使用
useCdn: process.env.NODE_ENV === 'production', // 是否使用CDN
}),
],
};// 在生产环境中
if (process.env.NODE_ENV === 'production') {
// 配置外部扩展选项 (让 webpack 防止 import的包被打包进dist文件 )
config.externals = {
// key -> 代码中 import from 后面的模版标识字符串
// value -> 替换在原地的变量名 (要和 cdn暴露在全局的变量名一致)
'bootstrap/dist/css/bootstrap.min.css': 'bootstrap',
'axios': 'axios'
}
}module.exports = config
-
webpack会给入口文件添加defer属性
<head> ... ... <script defer="defer" src=".././login/index.js"></script> ... ... </head>
多页面打包
单页面: 单个html文件, 切换DOM的方式实现不同业务逻辑展示
多页面: 多个html文件, 切换页面实现不同业务逻辑展示
步骤
-
需求: 把黑马头条-数据管理平台-内容页面一起引入打包使用
-
把 content.html 文件放入public文件夹,作为模版文件
-
准备src/content文件夹, 放入配套的css和js文件
-
把auth.js文件放到utils文件夹
-
配置 webpack 多入口 和 多页面的位置
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'),
// 文件名称
// [name]占位符, 读取entry 属性的key作为文件名
filename: './[name]/index.js',
},
// 注册插件
plugins: [
// 自动生成HTML插件(页面1)
new HtmlWebpackPlugin({
// 插件配置
template: path.resolve(__dirname, 'public/login.html'), // 模板文件
filename: path.resolve(__dirname, 'dist/login/index.html'), // 输出文件
// 自定义属性, 在html模版中 <%=htmlwebpackPlugin.options.useCdn%> 访问使用
useCdn: process.env.NODE_ENV === 'production', // 是否使用CDN
chunks: ['login'] // 引入哪些打包后的模块 (和 entry 属性的key一致)
}),
// 自动生成HTML插件(页面2)
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']
}),
// 提取css插件
new MiniCssExtractPlugin({
filename: './[name]/index.css', // 输出文件
}),
],
};module.exports = config
-
运行/打包后, 测试效果
步骤
-
需求: 把黑马头条-数据管理平台-发布文章页面一起引入打包使用
-
把 publish.html 文件放入public文件夹,作为模版文件
-
NPM包市场: npm | Home
-
准备src/publish文件夹, 放入配套的css和js文件
-
把editor.js文件放到utils文件夹
-
配置 webpack 多入口 和 多页面的位置
const config = {
// 自定入口文件
entry: {
... ...
'publish': path.resolve(__dirname, 'src/publish/index.js'),
},
... ...
// 注册插件(提供更多功能)
plugins: [
... ...
// 自动生成HTML插件(页面3)
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']
}),
... ...
],
... ...
};// 在生产环境中
if (process.env.NODE_ENV === 'production') {
// 配置外部扩展选项 (让 webpack 防止 import的包被打包进dist文件 )
config.externals = {
// key -> 代码中 import from 后面的模版标识字符串
// value -> 替换在原地的变量名 (要和 cdn暴露在全局的变量名一致)
'@wangeditor/editor': 'wangEditor',
'form-serialize': 'serialize'}
}module.exports = config
-
运行/打包后, 测试效果
分割公共代码
步骤
-
有些代码a页面用到了, b页面也用到了, 就需要提取出来, 而不是被重复打包
-
配置webpack的splitChunks分割功能
const config = {
// 优化配置
optimization: {
... ...
// 代码分割
splitChunks: {
chunks: 'all', // 所有模块动态非动态移入的都分割分析
cacheGroups: { // 分隔组
common: { // 提取公共模块
minSize: 0, // 抽取chunk最小字节大小
minChunks: 2, // 最小引用数
reuseExistingChunk: true, // 当前 chunk 包含已从主 bundel 中拆分出的模块, 则它将被重用
name(module, chunks, cacheGroupKey) { // 分离出模块文件名
const allChunksNames = chunks.map((item) => item.name).join('~') // 模块名: 1~模块名
return./js/${allChunksNames}
// 输出到 dist 目录下位置
},
},
},
},
},
};module.exports = config