webpack是一个JS的静态打包工具
静态模块: 编写代码过程中的, html, css, js, 图片等固定内容的文件
打包: 把静态模块的内容, 压缩, 整合, 转译等 (前端工程化)
转译: 把less/sass转成css代码, 把ESC6+ 降级成ES5, 支持多种模块标准语法

体验
npm init -y
npm i webpack webpack-cli --save-dev

npm run build

入口和出口
通过webpack配置, 影响打包的过程和结果
步骤
- 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
需求 : 点击登录按钮, 判断手机号和验证码程度

步骤
自动生成HTML
HtmlWebpackPlugin简化了HTML文件的创建, 以便为你的webpack包提供服务
步骤
- 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加载器进行解析
使用
- module.exports = {
- ... ...
- // 配置加载器
- module: {
- rules: [
- {
- test: /\.css$/i,
- use: ['style-loader', 'css-loader'],
- },
- ],
- },
- };
提取css代码
把css代码提出成单独文件, 可以减少js文件体积, 并且css文件可以被浏览器缓存
步骤
- 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"],
- },
- ],
- },
- };
压缩css代码
css代码提取后没有被压缩, 通过css-minimizer-webpack-pligin插件, 压缩自己的css文件
步骤
- 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代码
步骤
- 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)
配置解释
登录页
完成登录页的核心流程, 以及Alert警告框的使用
流程

步骤
代码
- /**
- * 完成登录功能
- * 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插件, 实现代码热更新
介绍
步骤
- module.exports = {
- // 配置打包模式(使用相关内置优化)
- mode: 'development',
- ... ...
- };
- "scripts": {
- ... ...
- "dev": "webpack server --open"
- },
打包模式
告诉webpack使用相应模式的内置优化

设置方式
- "scripts": {
- "build": "webpack --mode=production",
- "dev": "webpack server --open --mode=development"
- },
条件打包
在开发模式下应用 style-loader 内嵌css(更快), 在生产模式下提取css代码(并行下载)
方法1

方法2
- "scripts": {
- "build": "cross-env NODE_ENV=production ... ",
- "dev": "cross-env NODE_ENV=development ..."
- },
- 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
注入环境变量
cross-env 设置的环境变量只在Node,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),
- }),
- ],
- };
- /**
- * 在前端项目代码中
- * 开发模式下打印语句正常使用
- * 生产模式下打印语句失效
- */
- if (process.env.NODE_ENV === 'production') {
- console.log = function () { }
- }
- console.log('开发模式下可用, 生成模式下禁用');
开发环境调错
代码被压缩和混淆后, 无法正确定位源代码位置(行数和列数)
解决
- 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网络中的各个服务器中, 供用户就近请求获取, 减轻自己服务器请求压力, 就近请求物理延迟低, 配套缓存策略

步骤
- <head>
- ... ...
- <!-- 生成环境下使用cdn资源 -->
- <!-- 该模版语法由 HtmlWebpackPlugin 插件提供 -->
- <% 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>
- ... ...
- <!-- 生成环境下使用cdn资源 -->
- <% 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>
- 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
- <head>
- ... ...
-
- <script defer="defer" src=".././login/index.js">script>
- ... ...
- head>
多页面打包
单页面: 单个html文件, 切换DOM的方式实现不同业务逻辑展示
多页面: 多个html文件, 切换页面实现不同业务逻辑展示
步骤
- 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
步骤
- 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
分割公共代码
步骤
- 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