Webpack
1、Webpack简介以及体验
1.1、Webpack简介
Webpack本质上是一个用于现代JavaScript应用程序的静态模块打包工具
。
静态模块:指的是编写代码过程中的,html,css,js,图片等固定内容的文件。
打包:把静态模块内容,压缩,整合,转译等(前端工程化)
- 把less/sass转成css代码
- 把ES6+降级成ES5
- 支持多种模块标准语法
1.2、体验Webpack
-
要求:封装utils包,校验手机号和验证码长度,在src/index.js中使用,使用Webpack打包
-
步骤:
1、新建项目文件夹TestWebpack,初始化包环境,得到package.json文件
npm init -y
2、新建src源代码文件夹(书写代码)包括utils/check.js封装用户名和密码长度函数,引入到src/index.js进行使用
-
src/utils/check.js
//封装校验手机号长度和验证码长度的函数 export const checkPhone = phone => phone.length === 11 export const checkCode = code => code.length === 6
-
src/index.js
//准备项目和源码 import { checkCode, checkPhone } from "./utils/check.js" console.log(checkPhone('13999999999')) console.log(checkCode('123456789'));
3、下载webpack webpack-cli到项目(版本独立)
npm i webpack webpack-cli --save-dev
注意:虽然 webpack 是全局软件包,封装的是命令工具,但是为了保证项目之间版本分别独立,所以这次比较特殊,下载到某个项目环境下,但是需要把 webpack 命令配置到 package.json 的 scripts 自定义命令,作为局部命令使用
-
package.json
"scripts": { "build": "webpack" }
4、项目中运行工具命令,采用自定义命令的方式(局部命令)
npm run build
npm run 自定义命令名字
注意:实际上在终端运行的是build右侧的具体命名
5、自动产生dist分发文件夹(压缩和优化后,用于最终运行的代码)
-
2、修改Webpack打包入口和出口
Webpack配置:影响web pack打包过程和结果
步骤:
1、项目根目录,新建webpack.config.js配置文件
2、导出配置对象,配置入口,出口文件的路径
const path = require('path')
module.exports = {
entry: path.resolve(__dirname, 'src/login/index.js'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: './login/index.js'
}
}
3、重新打包观察(只有和入口产生直接/间接的引入关系,才会被打包)
3、自动生成HTML文件
我们在打包js文件时,它不会生成响应的HTML文件,这里我们就需要使用到自动生成HTML文件的插件html-webpack-plugin
在Webpack打包时生成html文件。
步骤:
1、下载html-webpack-plugin本地软件包
npm i html-webpack-plugin --save-dev
2、配置webpack.config.js让Webpack拥有插件功能
// ...
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// ...
plugins: [
new HtmlWebpackPlugin({
template: './public/login.html', // 模板文件
filename: './login/index.html' // 输出文件
})
]
}
3、重新运行打包命令
npm run build
4、打包css代码
当然我们在打包时还需要打包css文件,但是Webpack默认只识别js和JSON文件内容,因此我们使用加载器,让Webpack识别更多类型的内容。
在打包css代码时只要是使用这两个加载器:
- 加载器
css-loader
:解析css代码 - 加载器
style-loader
:把解析后的css代码插入到DOM
步骤:
1、准备css文件代码引入到src/login.index.js中(压缩转译处理等)
import './index.css'
import 'bootstrap/dist/css/bootstrap.min.css'
2、下载css-loader和style-loader本地软件包
npm i css-loader style-loader --save-dev
3、配置webpack.config.js让Webpack拥有该加载器功能
module.exports = {
// ...
module: { // 加载器
rules: [ // 规则列表
{
test: /\.css$/i, // 匹配 .css 结尾的文件
// 使用从后到前的加载器来解析 css 代码和插入到 DOM
use: ['style-loader', 'css-loader'],
}
]
}
};
4、运行打包查看打包效果
5、优化-提取css代码
我们用加载器的形式打包的css代码,我们可以发现它并没有生成css文件,而是整合到了js文件中,这样导致js文件体积变得很大,为解决这个问题我们就可以采用插件mini-css-extract-pligin
插件提起css代码,注意不能和style-loader一起使用。
步骤:
1、下载mini-css-extract-plugin本地软件包
npm i mini-css-extract-plugin --save-devb
2、配置webpack.config.js让Webpack拥有该插件功能
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
plugins: [
new MiniCssExtractPlugin()
],
module: {
rules: [
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
],
},
3、运行打包查看打包效果
6、优化-压缩过程
我们使用Webpack主要是为了把所有的文件都进行打包压缩,但是css代码我们并没有压缩只是把它提取成为了一个单独的代码文件,为了压缩css代码我们就需要使用css-minimizer-webpack-plugin
插件。
步骤:
1、下载mini-css-extract-plugin本地软件包
npm i css-minimizer-webpack-plugin --save-devb
2、配置webpack.config.js让Webpack拥有该插件功能
const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin')
optimization: {
minimizer: [
// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
// `...`,
new CssMinimizerPlugin(),
],
},
plugins: [new MiniCssExtractPlugin()],
3、运行打包查看打包效果
7、打包less代码
对于less代码我们也是使用加载器less-loader
对less代码进行打包,less-loader需要配合less软件包使用。
步骤:
1、新建less代码(设置背景图)并引入到src/login/index.js中
2、下载less和less-loader本地软件包
npm i less less-loader --save-devb
3、配置webpack.config.js让Webpack拥有该插件功能
const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin')
optimization: {
minimizer: [
// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
// `...`,
new CssMinimizerPlugin(),
],
},
plugins: [new MiniCssExtractPlugin()],
4、运行打包查看打包效果
8、打包图片
由于Webpack只能识别js文件,所以我们的在开发中使用的图片也是需要打包,在Webpack5中内置了资源模块的打包,无需下载额外 loader 。
步骤:
1、配置 webpack.config.js 让 Webpack 拥有打包图片功能,占位符 【hash】对模块内容做算法计算,得到映射的数字字母组合的字符串,占位符 【ext】使用当前模块原本的占位符,例如:.png / .jpg 等字符串,占位符 【query】保留引入文件时代码中查询参数(只有 URL 下生效)。
2、注意:判断临界值默认为 8KB
大于 8KB 文件:发送一个单独的文件并导出 URL 地址
小于 8KB 文件:导出一个 data URI(base64字符串)
3、在 src/login/index.js 中给 img 标签添加 logo 图片
/**
* 目标9:打包资源模块(图片处理)
* 9.1 创建 img 标签并动态添加到页面,配置 webpack.config.js
* 9.2 打包后观察效果和区别
*/
// 9.1 创建 img 标签并动态添加到页面,配置 webpack.config.js
// 注意:js 中引入本地图片资源要用 import 方式(如果是网络图片http地址,字符串可以直接写)
import imgObj from './assets/logo.png'
const theImg = document.createElement('img')
theImg.src = imgObj
document.querySelector('.login-wrap').appendChild(theImg)
4、配置 webpack.config.js 让 Webpack 拥有打包图片功能
// ...
module.exports = {
// ...
module: {
rules: [
// ...
{
test: /\.(png|jpg|jpeg|gif)$/i,
type: 'asset',
generator: {
filename: 'assets/[hash][ext][query]'
}
}
]
}
}
9、搭建开发环境
每次改动代码,都要重新打包,很麻烦,所以这里给项目集成 webpack-dev-server 开发服务器,主要作用是启动 Web 服务,打包输出源码在内存,并会自动检测代码变化热更新到网页。
步骤:
1、下载 webpack-dev-server 软件包到当前项目
npm i webpack-dev-server --save-dev
2、配置自定义命令,并设置打包的模式为开发模式
// ...
module.exports = {
// ...
mode: 'development'
}
"scripts": {
// ...
"dev": "webpack serve --mode=development"
},
3、使用 npm run dev 来启动开发服务器,访问提示的域名+端口号,在浏览器访问打包后的项目网页,修改代码后试试热更新效果
在 js / css 文件中修改代码保存后,会实时反馈到浏览器
10、打包模式
打包模式主要有两种模式,分别是开发模式和生产模式,主要是告知Webpack使用相应模式的内置优化。
模式名称 | 模式名字 | 特点 | 场景 |
---|---|---|---|
开发模式 | development | 调试代码,实时加载,模块热替换等 | 本地开发 |
生产模式 | production | 压缩代码,资源优化,更轻量等 | 打包上线 |
设置方式:
- 在webpack.config.js配置文件设置mode选项
- 在package.json命令行设置mode参数
命令行设置的优先级高于配置文件中的,推荐用命令行设置,推荐使用命令行方式。
-
如何设置影响 Webpack呢?
-
方式1:在 webpack.config.js 配置文件设置 mode 选项
// ... module.exports = { // ... mode: 'production' }
-
方式2:在 package.json 命令行设置 mode 参数
"scripts": { "build": "webpack --mode=production", "dev": "webpack serve --mode=development" },
-
-
体验:在 build 命令后 修改 mode 的值,打包输出观察打包后的 js 文件内容
11、打包模式的应用
在开发模式下用style-loader内嵌更快,在生产模式下提取css代码
方法:
- webpack.config.js 配置导出函数,但是局限性大(只接受 2 种模式)
- 借助 cross-env (跨平台通用)包命令,设置参数区分环境
- 配置不同的 webpack.config.js (适用多种模式差异性较大情况)
步骤:
1、下载 cross-env 软件包到当前项目
npm i cross-env --save-dev
2、配置自定义命令,传入参数名和值(会绑定到 process.env 对象下)
"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"
},
3、在 webpack.config.js 区分不同环境使用不同配置
module: {
rules: [
{
test: /\.css$/i,
// use: ['style-loader', "css-loader"],
use: [process.env.NODE_ENV === 'development' ? 'style-loader' : MiniCssExtractPlugin.loader, "css-loader"]
},
{
test: /\.less$/i,
use: [
// compiles Less to CSS
process.env.NODE_ENV === 'development' ? 'style-loader' : MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
],
}
],
},
4、重新打包观察两种配置区别
12、前端-注入环境变量
cross-env 设置的只在 Node.js 环境生效,前端代码无法访问 process.env.NODE_ENV。为解决这个问题我们可以使用 Webpack 内置的 DefinePlugin 插件,在编译时,将前端代码中匹配的变量名,替换为值或表达式,配置 webpack.config.js 中给前端注入环境变量。
// ...
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)
})
]
}
13、开发环境调错-source map
代码被压缩和整合之后,我们无法正确定位源代码的位置(行数和列数),面对代码的报错我们找不到问题的出处,为解决这个问题,webpack推出了source map这个功能,它可以准确追踪error和warning在原始代码的位置。只需要在webpack.config.js中配置devtool选项。
//...
module.exports = {
//...
devtool: 'inline-source-map'
}
inline-source-map 选项:把源码的位置信息一起打包在 JS 文件内
注意:source map 适用于开发环境,不要在生产环境使用(防止被轻易查看源码位置)
14、解析别名
我们在引入模块时,模块的路径或者名称存在共同部分,引入模块就有大量代码重复,这时候我们就可以使用接卸别名,把公共部分提取出来,用指定的别名来代替共同部分,让模块的引用变得更加简单。
演示:
1、原来的引用模块路径
import { checkPhone, checkCode } from '../src/utils/check.js'
2、在webpack.config.js中配置解析别名
//...
const config = {
//...
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
}
}
3、配置以后我们引入模块
import { checkPhone, checkCode } from '@utils/check.js'
15、优化-CND使用
CDN是指内容分发网络,指的是一组分布在各个地区的服务器,它的作用是把静态资源文件/第三方库放在CDN网络中各个服务器中,供用户就近请求获取,它能够减轻自己服务器请求压力,就近请求物理延迟,配套缓存策略。
步骤:
1、在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">
<% } %>
2、配置 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 = {
// ...
plugins: [
new HtmlWebpackPlugin({
// ...
// 自定义属性,在 html 模板中 <%=htmlWebpackPlugin.options.useCdn%> 访问使用
useCdn: process.env.NODE_ENV === 'production'
})
]
}
16、多页面打包
单页面是指单个html文件,切换DOM的方式实现不同业务逻辑展示。
多页面是指多个html文件,切换页面实现不同业务逻辑展示。
配置 webpack.config.js 多入口和多页面的设置
// ...
const config = {
entry: {
'模块名1': path.resolve(__dirname, 'src/入口1.js'),
'模块名2': path.resolve(__dirname, 'src/入口2.js'),
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: './[name]/index.js'
}
plugins: [
new HtmlWebpackPlugin({
template: './public/页面2.html', // 模板文件
filename: './路径/index.html', // 输出文件
chunks: ['模块名2']
})
new HtmlWebpackPlugin({
template: './public/页面2.html', // 模板文件
filename: './路径/index.html', // 输出文件
chunks: ['模块名2']
})
]
}
17、优化-分隔公共代码
当页面数量比较多的时候,一定会存在公共代码,分隔公共代码主要是为了将页面中的公共部分单独分隔出来,从而进一步压缩代码。
配置 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 目录下位置
}
}
}
}