Webpack

本节目标

  • 基础配置

基础配置

webpack是一个JS的静态打包工具

静态模块: 编写代码过程中的, html, css, js, 图片等固定内容的文件

打包: 把静态模块的内容, 压缩, 整合, 转译等 (前端工程化)

转译: 把less/sass转成css代码, 把ESC6+ 降级成ES5, 支持多种模块标准语法

体验

  1. 新建并初始化项目, 编写业务源代码

npm init -y

  1. 下载webpack webpack-cli 到当前目录中(版本独立), 并配置局部自定义命令

npm i webpack webpack-cli --save-dev

  1. 运行打包命令, 自动产生dist分发文件夹(压缩和优化后, 用于最终运行的代码)

npm run build

  1. 执行过程

入口和出口

通过webpack配置, 影响打包的过程和结果

步骤

  1. 官网: 概念 | webpack 中文文档 | webpack中文文档 | webpack中文网

  2. 项目根目录, 新建webpack.config.js配置文件

  3. 导出配置对象, 配置入口/出口文件的路径

  4. 只有和入口产生直接/间接的引入关系, 才会被打包

    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

需求**:**点击登录按钮, 判断手机号和验证码程度

步骤

  1. 准备登录页面(index.html)
  2. 编写核心JS逻辑代码(index.js), 打包
  3. 手动复制网页到dist中,
  4. 在页面中引入打包后的js, 运行

自动生成HTML

HtmlWebpackPlugin简化了HTML文件的创建, 以便为你的webpack包提供服务

步骤

  1. 位置: 官网搜索HtmlWebpackPlugin

  2. 安装: npm install --save-dev html-webpack-plugin

  3. 配置

    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加载器进行解析

使用

  1. 位置: 官网搜索css-loader

  2. css-loader( 解析css ) 和 style-loader( 把解析后的css插入到DOM ) 配合使用

  3. 安装 npm install css-loader style-loader --save-dev

  4. 引入css文件到src/login/index.js中

  5. 配置webpack.config.js 让其拥有该加载器的功能

    module.exports = {
    ... ...
    // 配置加载器
    module: {
    rules: [
    {
    test: /.css$/i,
    use: ['style-loader', 'css-loader'],
    },
    ],
    },
    };

  6. 安装 npm i bootstrap

  7. 引入 import 'bootstrap/dist/css/bootstrap.min.css'

提取css代码

把css代码提出成单独文件, 可以减少js文件体积, 并且css文件可以被浏览器缓存

步骤

  1. 位置: 官网搜索MiniCssExtractPlugin

  2. 使用mini-css-extract-plugin插件提取css

  3. 下载命令: npm install --save-dev mini-css-extract-plugin

  4. 配置文件: 在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"],
    },
    ],
    },
    };

  5. 注意: 该插件不能和style-loader一起使用

压缩css代码

css代码提取后没有被压缩, 通过css-minimizer-webpack-pligin插件, 压缩自己的css文件

步骤

  1. 位置: 官网搜索CssMinimizerWebpackPlugin

  2. 安装: npm install css-minimizer-webpack-plugin --save-dev

  3. 配置

    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代码

步骤

  1. 位置: 官网搜索less-loader

  2. 新建 index.less代码文件, 设置背景图, 引入代码文件

  3. 下载: npm install less less-loader --save-dev

  4. 注意 less-loader 需要配置less软件包使用

  5. 配置

    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)

配置解释

  1. 位置: 官网搜索 '资源模块'
  2. test: 配置文件类型( 根据后缀名匹配文件 )
  3. type: 配置打包类型( asset表示自动适配 )
  • 自动适配的依据是文件大小, 判断临界默认值为8kb
  • 大于8kb的文件: 发送一个单独的文件并导出URL地址( 拷贝图片 )
  • 小于8kb的文件: 导出一个data URL(base64字符串)
  1. filename: 配置打包的出口( 打包后文件存放位置 )
  • 占位符 [hash] 对模块内容做算法计算, 得到映射的数字字母组合的字符串( 生成文件名 )
  • 占位符 [ext] 使用当前模块原本的占位符, 例如 .png .jpg等字符串 ( 沿用文件后缀 )
  • 占位符 [query] 保留引入文件时代码中查询参数( 只有URL下有效 ) ( 不太常用 )

登录页

完成登录页的核心流程, 以及Alert警告框的使用

流程

步骤

  1. 下载使用axios (npm i axios)
  2. 准备并修改utils工具包下的文件 (导出函数)
  3. 编写逻辑代码, 打包后运行

代码

复制代码
/**
 *  完成登录功能 
 *  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 目录下的内容

步骤

  1. 下载 npm install --save-dev webpack webpack-dev-server

  2. 设置 设置默认为开发模式

    module.exports = {
    // 配置打包模式(使用相关内置优化)
    mode: 'development',
    ... ...
    };

  3. 配置 配置自定义命令

    "scripts": {
    ... ...
    "dev": "webpack server --open"
    },

  4. 启动 使用自定义命令启动开发服务器,体验代码热更新

打包模式

告诉webpack使用相应模式的内置优化

设置方式

  1. 在webpack.config.js配置文件中设置mode选项

  2. 在package.json命令行设置mode参数 (推荐)

  3. 命令行设置的优先级高于配置文件的

    "scripts": {
    "build": "webpack --mode=production",
    "dev": "webpack server --open --mode=development"
    },

条件打包

在开发模式下应用 style-loader 内嵌css(更快), 在生产模式下提取css代码(并行下载)

方法1

  1. webpack.config.js配置导出函数

  2. 具体代码参考官网, 搜索模式(Mode)

  3. 缺点: 局限性大, 只能区分2中模式(开发/生产)

方法2

  1. 借助cross-env(跨平台通用) 包命令, 设置参数区分环境

  2. 安装 npm i cross-env --save-dev

  3. 配置自定义命令, 传入参数名和值 ( 会绑定到process.env对象中 ) (Node.js环境变量)

    "scripts": {
    "build": "cross-env NODE_ENV=production ... ",
    "dev": "cross-env NODE_ENV=development ..."
    },

  4. 在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',
    ],
    },
    ],
    },
    };

  5. 重新打包

  6. 缺点: 如果不同模式的配置差异很大, 判断和配置起来就会比较麻烦

方法3

  1. 配置不同的webpcak.config.js
  2. 适合多种模式差异性较大的情况

注入环境变量

cross-env 设置的环境变量只在Node,js环境生效, 前端代码无法访问

解决

  1. 使用webpack内置的 DefinePlugin插件

  2. 位置 官网搜索 DefinePlugin

  3. 作用 在编译时, 将前端代码中匹配的变量名, 替换为值或表达式

  4. 配置

    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),
    }),
    ],
    };

  5. 使用

    /**

    • 在前端项目代码中
    • 开发模式下打印语句正常使用
    • 生产模式下打印语句失效
      */
      if (process.env.NODE_ENV === 'production') {
      console.log = function () { }
      }
      console.log('开发模式下可用, 生成模式下禁用');

开发环境调错

代码被压缩和混淆后, 无法正确定位源代码位置(行数和列数)

解决

  1. source map(资源地图) : 可以准确追踪error和warning在原始代码位置

  2. inline-source-map选项: 把源代码的位置信息一起打包在JS文件内

  3. 其他选项: 官网搜索 devtool 查看

  4. 注意: 该配置仅适用于开发环境, 不要在生产环境使用(防止被轻易查看源码位置)

    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网络中的各个服务器中, 供用户就近请求获取, 减轻自己服务器请求压力, 就近请求物理延迟低, 配套缓存策略

步骤

  1. 需求: 开发模式使用本地的第三方库, 生产模式下使用CDN加载引入

  2. CDN服务商: BootCDN - Bootstrap 中文网开源项目免费 CDN 加速服务

  3. 在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>
  4. 在 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

  5. webpack会给入口文件添加defer属性

    <head> ... ... <script defer="defer" src=".././login/index.js"></script> ... ... </head>

多页面打包

单页面: 单个html文件, 切换DOM的方式实现不同业务逻辑展示

多页面: 多个html文件, 切换页面实现不同业务逻辑展示

步骤

  1. 需求: 把黑马头条-数据管理平台-内容页面一起引入打包使用

  2. 把 content.html 文件放入public文件夹,作为模版文件

  3. 准备src/content文件夹, 放入配套的css和js文件

  4. 把auth.js文件放到utils文件夹

  5. 配置 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

  6. 运行/打包后, 测试效果

步骤

  1. 需求: 把黑马头条-数据管理平台-发布文章页面一起引入打包使用

  2. 把 publish.html 文件放入public文件夹,作为模版文件

  3. CDN服务商: https://unpkg.com/form-serialize@0.7.2/index.js

  4. NPM包市场: npm | Home

  5. 准备src/publish文件夹, 放入配套的css和js文件

  6. 把editor.js文件放到utils文件夹

  7. 配置 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

  8. 运行/打包后, 测试效果

分割公共代码

步骤

  1. 有些代码a页面用到了, b页面也用到了, 就需要提取出来, 而不是被重复打包

  2. 配置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

相关推荐
申阳7 分钟前
Day 7:05. 基于Nuxt开发博客项目-首页开发
前端·后端·程序员
Crystal32815 分钟前
App端用户每日弹出签到弹窗如何实现?(uniapp+Vue)
前端·vue.js
摸着石头过河的石头16 分钟前
Service Worker 深度解析:让你的 Web 应用离线也能飞
前端·javascript·性能优化
用户40993225021217 分钟前
Vue 3中watch侦听器的正确使用姿势你掌握了吗?深度监听、与watchEffect的差异及常见报错解析
前端·ai编程·trae
1024小神19 分钟前
Xcode 常用使用技巧说明,总有一个帮助你
前端
政采云技术1 小时前
音视频通用组件设计探索和应用
前端·音视频开发
不爱吃糖的程序媛1 小时前
Electron 如何判断运行平台是鸿蒙系统(OpenHarmony)
javascript·electron·harmonyos
Hilaku1 小时前
我用AI重构了一段500行的屎山代码,这是我的Prompt和思考过程
前端·javascript·架构
Cxiaomu1 小时前
React Native App 自动检测版本更新完整实现指南
javascript·react native·react.js
IT_陈寒2 小时前
Vite性能优化实战:5个被低估的配置让你的开发效率提升50%
前端·人工智能·后端