【学习笔记】Webpack基础(一)

94 阅读6分钟

Webpack基础(一)

什么是Webpack

模块化出现以后,代码被分离到各个模块中,由此一来,每个文件都需要请求服务器来获取,加载速度慢。Webpack就是为了解决这个问题。

从下面的图片可以看出,Webpack的主要功能就是把所有小文件打包成一个或多个大文件。将项目中的模块化代码(如 JavaScript、CSS、图片等资源)打包为可部署的静态文件。

除此之外,它还有代码分割、资源优化、提升开发体验等功能。

image.png

建议打开熟悉的项目的webpack配置文件,对照文件学习和理解。

安装依赖

安装webpack以及webpack-cli

  • webpack是核心包
  • webpack-cli 开发者与 Webpack 之间的桥梁,交互工具,提供命令行接口,让开发者可以调用 Webpack

设置运行模式

通过配置对象的 mode 字段设置运行模式,有三个可选项,默认为'none'

  • production:用于生产环境,优化输出代码的体积和性能
  • development:用于开发环境,优化构建速度和调试体验
  • none:不使用默认优化

具体的行为对比(扩展:帮助理解)

特性developmentproductionnone
代码压缩❌ 不启用✅ 启用❌ 不启用
SourceMap✅ 默认开启(evalcheap-module-source-map❌ 默认关闭❌ 默认关闭
环境变量process.env.NODE_ENV = 'development'process.env.NODE_ENV = 'production'不设置
优化不进行性能优化✅ 启用 Tree Shaking 和 Scope Hoisting❌ 不进行优化
打包速度较慢(因为有优化)取决于具体配置

基本配置

  • Webpack 使用CommonJS模块语法导入导出
  • entry入口文件:表示从哪个文件开始解析依赖关系
  • output.path 必须是绝对路径,使用path.join(__dirname, 'dist')可以动态生成绝对路径
const path = require("path");
module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    filename: "bundle.js",
    path: path.join(__dirname, "dist"), // 指定输出文件夹
  },
};

Loader

Loader 就是将 Webpack 不认识的内容转换为认识的内容

Webpack 默认支持处理 JS 与 JSON 文件,其他类型的文件都处理不了,因此借助 Loader 进行各种其他类型文件的处理

引入样式文件 常用 loader:

  • css-loader: 将 CSS 转换为 JavaScript 模块,允许在 JavaScript 中导入 CSS
import "./styles.css";
  • style-loader: 将解析后的 CSS(由css-loader处理后)注入到页面的<style>标签里。(在开发环境中,通常用于在页面加载时将 CSS 直接注入,避免产生多余的文件请求)
  • postcss-loader: 自动添加 CSS3 部分属性的浏览器前缀
  • 处理 less 或 sass 文件的 loader,比如 less 文件加载使用less-loader
module.exports = {
  // ...
  module: {
    rules: [
      // 转换规则
      {
        test: /\.(le|c)ss$/i, //匹配所有的 sass/scss/css 文件
        use: ["style-loader", "css-loader", "postcss-loader", "less-loader"],
      },
    ],
  },
};

一般在生产环境不使用 style-loader,而是使用 mini-css-extract-plugin,将 css 以文件的形式引入到页面上 use: [ // 'style-loader', MiniCssExtractPlugin.loader, // 添加 loader 可以在 plugin 中配置文件名称等 'css-loader', 'postcss-loader', 'sass-loader', ]

插件

插件可以贯穿 Webpack 打包的声明周期,执行不同的任务,常用的插件如下:

  • html-webpack-plugin:使打包后的 js 或 css 文件自动引入到 html 文件中
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    filename: "bundle.js",
    path: path.join(__dirname, "dist"), // 指定输出文件夹
  },
  module: {
    rules: [
      // 转换规则
      {
        test: /\.css$/, // 匹配所有的css文件
        use: "css-loader", // use:对应的Loader名称
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
  ],
};
  • clean-webpack-plugin: 打包前将打包目录清空

区分环境

本地开发和部署线上,通常有不同的需求,需要在代码中判断出当前环境,可以通过cross-env设置环境变量

  1. 安装
npm install cross-env -D
  1. 设置环境变量,可以在package.json脚本中设置环境变量

这里提到的环境变量是当前运行的 Node.js 应用的临时环境变量,程序关闭后失效,并不等同于电脑系统环境变量

"scripts": {
    "dev": "cross-env NODE_ENV=dev webpack serve --mode development",
    "test": "cross-env NODE_ENV=test webpack --mode production",
    "build": "cross-env NODE_ENV=prod webpack --mode production"
  }
  1. 在代码中使用环境变量

应用中设置的临时环境变量保存在process.env中:

const curEnv = process.env.NODE_ENV; // 值为在脚本中设置的对应的值
  1. cross-env工作原理(扩展:帮助理解) cross-env本质上是一个命令行工具。它在后台会检查当前操作系统,并动态生成符合该系统规范的命令,然后运行你设置的脚本。它就是为了解决环境变量设置的跨平台兼容问题。

启动 devServer

直接打开 HTML 文件虽然可以展示内容,但无法支持模块化、跨域请求等现代开发需求。需要一个 HTTP 服务来托管代码。 devServer是 Webpack 的一个插件模块,由webpack-dev-server提供支持。它的核心功能是:

  • 本地开发服务器:启动一个基于 Node.js 的 HTTP 服务器。
  • 实时更新:结合 Webpack 的热更新(HMR, Hot Module Replacement)功能,可以在代码修改时自动刷新页面,或局部跟新代码,不需要手动刷新浏览器
  • 代理服务:前端代码和后端接口通常运行在不同的域,跨域问题可能导致接口无法调用。通过 devServer 的代理配置,可以将 API 请求代理到后端服务器,解决跨域问题
  • 优化开发体验:简化了手动编译和刷新流程,提升开发效率

通过devServer,可以在本地直接运行未打包的代码,同时看到实时效果

const devServer = "192.168.101.75";
module.exports = {
  //...
  devServer: {
    // host: "localhost", // 只能在本机启动
    host: "0.0.0.0", // 局域网内其他主机可以访问
    port: 9999,
    hot: true, // 开启HMR功能,用于开发环境
    open: true, // 启动服务器时,浏览器打开默认访问地址
    historyApiFallback: true, // 解决react-router刷新404问题,重定向到index.html,由客户端路由来处理这些请求
    // 接口代理转发
    proxy: [
      {
        context: ["/public", "/file", "/api"], // 当请求路径匹配到这些前缀时会被转发到target中指定的服务器地址
        target: `http://${devServer}`,
        changeOrigin: true, // 允许修改请求头中的 Origin,解决一些后端服务器要求的跨域验证问题
      },
    ],
  },
};

新特性:资源模块(Webpack 5)

webpack5 之前,需要file-loaderurl-loaderimg-loader等处理图片和字体文件,webpack5 新增了资源模块,允许使用资源文件(字体、图标等),而无需配置额外的 loader。

资源模块的type支持以下四种类型:

  1. asset/resource
  • 将资源打包为单独的文件,并导出文件的URL,类似于的file-loader
  • 示例:
import flowerImg from './flower.png';
console.log(flowerImg); // 输出:生成后的路径,例如 /dist/image.png
  1. asset/inline
  • 将资源转为Data URL的形式直接内联到代码中,类似于url-loader在limit小于某值时的功能
  • 适合小文件,图标/SVG等,减少HTTP请求
  • 示例:
import icon from './icon.svg';
console.log(icon); 
// 输出:'data:image/svg+xml;base64,...'

  1. asset/source
    • 将资源以源码字符串的形式导出,类似于raw-loader
    • 适合需要直接使用文件内容的场景,例如读取Markdown文件、文本文件或SVG的原始XML数据
    • 示例:
import content from './example.txt';
console.log(content); 
// 输出:'Hello, Webpack!'

  1. asset
    • 自动在asset/resourceasset/inline之间切换
    • 可以自定义文件大小阈值阈值
    • 示例:
import img from './image.png';
console.log(img);
// 如果小于 8 KB,输出:'data:image/png;base64,...'
// 如果大于 8 KB,输出:'http://example.com/image.png'

asset配置示例:

module.exports = {
  // ...
  module: {
    {
      test: /\.(jpe?g|png|gif)$/i,
      type: 'asset',
      generator: {
        // 输出文件位置以及文件名
        // [ext] 自带 "." 这个与 url-loader 配置不同
        filename: "[name][hash:8][ext]"
      },
      parser: {
        dataUrlCondition: {
          maxSize: 50 * 1024 //超过50kb不转 base64
        }
      }
    },
    {
      test: /\.(ttf|woff2?|map4|map3|avi|xlsx)$/,
      type: "asset/resource", // 原封不动的输出
      generator: {
        filename: "static/media/[name]-[hash:6][ext]",
      },
    },
  }
}

JS兼容性(Babel)

在开发中我们想使用最新的 Js 特性,但是有些新特性的浏览器支持并不是很好,所以 Js 也需要做兼容处理,常见的就是将 ES6 语法转化为 ES5。此时需要使用Babel。

  1. 安装依赖
  • babel-loader 使用Babel加载ES2015+代码,并将其转换为ES5
  • @babel/core Babel编译的核心包
  • @babel/preset-env Babel编译的预设
  1. 配置loader
const config = {
  // ...
  module: { 
    rules: [
      {
        test: /\.js$/i,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: [
                '@babel/preset-env'
              ],
            }
          }
        ]
      },
    ]
  },
}

可以将Babel配置文件提取出来,在目录下新增.babelrc.js文件

SourceMap

SourceMap是一种映射关系,方便在代码出现问题时,定位到源码位置。

推荐配置:

开发环境cheap-module-source-map调试体验好,性能较优

  • cheap: 只定位到行,不定位列,生成速度快
  • module: 适合模块化项目

生产环境 (none) 保护源码,放置泄露

hash

使用 Webpack 打包时,每个资源都可以生成一个带 hash 的路径。比如在基础配置中用到的filename: "[name][hash:8][ext]".。

为什么要使用 hash 呢?假设不使用 hash,并开启了缓存,则即使代码发生了修改,资源路径并没有改变,命中缓存,浏览器展示的始终是未改之前的内容。

除非关闭浏览器缓存,否则,每次文件更改,需要改变资源生成路径,来使浏览器资源更新。以便可以浏览到最新内容。

三种可选的 hash 类型

类型定义特性适用场景
hash基于整个项目的构建生成一个全局的哈希值- 每次构建时,所有文件的哈希值相同
- 任意代码变动会导致整体哈希值变化
- 输出非分块文件。如:index.html.
- 不适合缓存优化
chunkhash基于每个 chunk(代码块)的内容生成哈希值。- 每个 chunk 独立的哈希值,互不影响。
- 仅内容变更的 chunk 的哈希值会变化。
- 分离的 JavaScript 文件,如vendor.jsmain.js
- 用于优化缓存
contenthash基于文件内容生成哈希值- 哈希值仅与文件自身内容相关
- 文件内容不变时,哈希值保持不变。
- 静态资源文件,如 CSS、图片等。
- 避免不必要的缓存更新。