Skip to content
大纲

基础配置

模块解析

webpack 内部使用 enhance-resolve 解析文件路径, 通过 resolve 字段可以配置解析策略

配置解析策略

resolve.alias

指定模块的别名, 以简写某些常用模块的路径

  • 通常使用 @ 表示 src 目录
  • 使用 $ 特殊字符结尾表示精确匹配

配置示例

js
module.exports = {
  // ...others
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "src/"),
      "@utils$": path.resolve(__dirname, "src/util/index.js"),
    },
  },
};

使用示例

js
import Comps from "@/comps";
import utils from "@utils";

resolve.extensions

解析未指定扩展名的文件时, 自动解析确定的扩展

  • 默认配置 [".js", ".json"] (新版的文档中删除了该说明, 可能不准确)
  • 配置该选项时会覆盖默认配置
  • 配置靠前的字段优先级更高
  • 使用 * 字段可以匹配任意文件扩展名, 不建议使用
  • 常用的配置: ['js', 'jsx', 'ts', 'tsx', 'json']
  • 不推荐添加非逻辑文件扩展名, 如 'css'

resolve.modules

指定解析模块时搜索的目录, 通常在引用自定义模块时需要修改该配置

  • 默认配置为 ['node_modules']
  • 可以配置多个目录
  • 配置靠前的目录优先级更高

配置示例

js
module.exports = {
    // ...
    resolve: {
        modules: [path.resolve(__dirname, 'my_modules'), 'node_modules]
    }
}

resolve.mainFields

导入模块时, 指定依据 'package.json' 中哪个字段导入模块, 通常不会修改该配置

  • 默认配置为 ['module', 'main']
  • 配置靠前的字段优先级更高

resolve.mainFiles

解析目录时, 解析目录下默认的文件, 通常不会修改该配置

  • 默认配置 ['index']
  • 配置靠前的字段优先级更高

解析流程

  1. 解析路径
    • 绝对路径
      • 场景示例: import '/my-project/src/resolve/path/some-module
      • 解析方式: 不处理, 直接使用
    • 相对路径
      • 场景示例: import '../utils/some/file'
      • 解析方式: 将引用文件所在目录作为上下文目录, 并将被引用文件路径解析为绝对路径
    • 模块路径
      • 场景示例:
        • import '@path/some-module'
        • import 'some-module'
      • 解析方式
        • 若使用路径别名, 则依据 resolve.alias 配置进行解析
        • 依据 resolve.modules 配置中指定的目录进行搜索解析
  2. 解析文件
    • 若解析路径指向文件
      • 指定扩展名: 直接加载指定文件
      • 省略扩展名: 依据 resolve.extensions 选项解析文件扩展名
    • 若解析路径指向目录
      • 目录下存在 'package.json' 文件
        1. 按照 'package.json' 文件中 main 字段解析文件
        2. 若不存在 main 字段或解析失败, 则按照 resolve.mainFields 配置中指定字段依此解析
      • 目录下缺少 package.json 文件
        1. 按照 resolve.mainFiles 配置依此查找文件
        2. 未配置 resolve.mainFiles 则默认查找 'index.js' 文件

入口配置

webpack 使用 entry 字段指定构建的入口

  • 配置格式
    • entry: string | [string]: 配置单个入口, 使用默认的 chunk 名称
    • entry: { [name: string]: string | [string] }: 配置单个或多个入口, 使用键作为 chunk 名称
    • entry: () => string | [string]: 配置动态入口, 返回一个配置项
  • 路径解析
    • 入口路径为单一字符串, 则依此路径解析
    • 入口路径为字符串数组, 则依此进行解析并打包至一个文件中
  • 默认配置
    • 默认入口: ./src/index.js
    • 默认名称: main
  • 使用场景
    • 单个入口通常用于构建单页面应用、库等
    • 多个入口通常用于构建多页面应用, 实现单页面应用长效缓存, 大型单页面应用的拆分等
  • 注意事项
    • 动态加载模块不是入口起点
    • 长效缓存值将修改频率极低的文件单独构建以避免修改文件名称, 从而充分利用浏览器缓存机制的处理方式

示例

js
module.exports = {
  entry: {
    shim: ["./src/shim", "./src/extension"], // 作为长效缓存, 对 shim 环境单独构建, 这里假设存在自定义的原生扩展
    main: "./src/index", // 构建的主入口
  },
};

出口配置

webpack 使用 output 字段配置输出的内容, 基本配置有

  • filename: 指定输出的文件名, 支持占位符模板, 默认 '[name].js'
  • path: 指定输出的目录, 需为绝对路径, 默认 __dirname + '/dist'
占位符(模板)说明
[name]模块名称,对应 entry 配置中的 chunk 名称
[id]模块标识符,每个 chunk 拥有一个唯一的 id 标志
[hash]模块标识符的 hash 值
[chunkhash]chunk 内容的 hash 值

示例

js
const path = require("path");
module.exports = {
  // ...
  output: {
    filename: `[name].[hash].js`,
    path: path.resolve(__dirname, "dist"),
  },
};

其他常用配置有 publicPath, library, libraryTarget, libraryExport 等, 这些配置与具体的使用场景相关, 详见 生产环境 部分

资源管理

webpack 中通过对应的 loader 允许使用 import 导入非 js 文件

配置 loader

webpack 通过 module.rules 属性配置 loader 规则, 该字段接收一个数组, 每一项为一条 loader 规则

示例

js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
};

规则

每条规则分为三部分: 条件, 结果, 嵌套规则

  • 条件: 有两种输入值, 其路径已根据 resolve 规则解析
    • resource: 请求文件的绝对路径, 配置字段有 test, include, excluderesource
    • issuer: 导入请求文件的模块的绝对路径, 相关字段为 issuer, 通常不需要配置
  • 结果: 有两种输入值, 在规则条件匹配时使用
    • loader: 应用在 resourve 上的 loader 数组, 配置字段有 loader, options, use, enforce
    • parser: 用于为模块创建解析器的选项对象, 配置字段为 parser
  • 嵌套规则: 可以使用 rulesoneOf 指定嵌套规则

配置规则条件

条件种类

  • { test: Condition }: 匹配特定条件, 该值通常是正则或正则数组
  • { include: Condition }: 匹配特定条件, 该值通常是字符串或字符串数组
  • { exclude: Condition }: 排除特定条件, 该值通常是字符串或字符串数组
  • { and: [Condition] }: 数组中所有条件满足时匹配
  • { or: [Condition] }: 数组中任一条件满足时匹配
  • { not: [Condition] }: 排除数组中条件

条件类型

  • 字符串: 目录绝对路径或文件绝对路径
  • 正则: test 输入值
  • 函数: 调用输入的函数, 必须返回一个 truthy
  • 条件数组: 至少一个匹配条件
  • 对象: 匹配所有属性, 每个属性有一个定义行为

示例

js
const path = require('path)
module.exports = {
    // ...
    module: {
        rules: [
            {
                test: /\.jsx?$/,
                include: [path.resolve(__dirname, 'src')]
            }
        ]
    }
}

配置规则结果

  • use: 指定应用的 loader, 执行顺序为从后向前
  • loader: { use: [{ loader }] } 的简写
  • options: 配置 loader 中的选项
  • enforce: 指定 loader 的种类, 取值为 'pre''post', 该值会影响 loader 调用顺序

配置示例

**安装 loaders **

bash
npm i --save-dev style-loader css-loader sass-loader node-sass
npm i --save-dev file-loader url-loader
npm i --save-dev csv-loader xml-loader

webpack.config.js

js
module.exports = {
  // ...
  module: {
    rules: [
      { test: /\.css$/, use: ["style-loader", "css-loader"] },
      {
        test: /\.sass/,
        use: ["style-loader", "css-loader", "sass-loader"],
      },
      {
        test: /\.(png|jpg|gif)/,
        use: [
          {
            loader: "url-loader",
            options: {
              limit: 8192, // 配置转换大小
              // fallback: 'file-loader' // 默认使用 file-loader
            },
          },
        ],
      },
      { test: /\.(woff|woff2|eot|ttf|otf)$/, use: ["file-loader"] },
      { test: /\.(csv|tsv)$/, use: ["csv-loader"] },
      { test: /\.xml$/, use: ["xml-loader"] },
    ],
  },
};

一些说明

  • 样式处理
    • style-loader: 用于将 css 文件转换为在执行时会插入 <style> 标签的脚本
    • css-loader: 用于加载 css 文件
    • sass-loader: 用于加载 sass 文件
  • 文件处理
    • file-loader: 用于加载图片, 字体等多种文件
    • url-loader: 可以对文件大小进行判断是否转换为文件
      • 若文件小于指定大小,则会转换为 Data Url 的格式,该选项通过 limit 配置
      • 若文件大于指定大小,可以通过 fallback 选项指定用来处理的 loader,默认使用 file-loader
    • image-webpack-loader: 用于加载图片, 提供更高级的压缩处理等配置, 可参考 文档
  • 数据处理
    • csv-loader: 用于处理 csv 格式文件, 数据内容会转换为对象
    • xml-loader: 用于处理 xml 格式文件, 数据内容会转换为对象
    • JSON 格式文件默认支持, 无需 loader

全局依赖

不推荐使用全局内容, 这里仅列出可能需要配置全局内容的情况。

全局常量

使用 DefinePlugin 创建在编译时可配置的全局常量, 该插件通常用于在代码中区分开发模式和生产模式

webpack.config.js

js
module.exports = {
  // ...others
  plugins: [
    new webpack.DefinePlugin({
      "process.env.NODE_ENV": JSON.stringify("development"),
      PRODUCTION: JSON.stringify(false), // const PRODUCTION = false
      BROWSER_SUPPORTS_HTML5: true, // const BROWSER_SUPPORTS_HTML5 = 'true'
    }),
  ],
};

Shimming

shimming 即垫片器, 用于提前初始化全局环境,一般用于以下情况

  • 引用的第三方模块需要使用潜在的全局变量
  • 加载 polyfill 以兼容低版本的浏览器

shiming 全局变量

使用 ProvidePlugin 插件提前初始化全局环境

js
const webpack = require('webpack')

// 引入模块
new webpack.ProvidePlugin({
    globalName: 'moduleName'
})

// 引入单个导出值
new webpack.ProvidePlugin({
    globalName: ['moduleName', 'propertyName', 'childPropertyName', ...]
})

示例: 引入全局 lodash 及其 map 方法

js
const webpack = require("webpack");

module.exports = {
  // ...others
  plugins: [
    new webpack.ProvidePlugin({
      _: "lodash",
      map: ["lodash", "map"],
    }),
  ],
};

细粒度 Shimming

模块中的 this 指向 module.exports, 若期望其指向 window 对象, 可通过 imports-loader 修改模块内的指向

示例: 指定 needWindow 模块的全局变量为 window

js
module.exports = {
  module: {
    rules: [
      {
        test: /neeWindow\.js$/,
        use: "imports-loader?this=>window",
      },
    ],
  },
};

加载 polyfills

polyfills 可通过 import 直接在主 bundle 中引入, 但通常会提取为独立文件并在浏览器中判断是否需要引入

polyfills.js

js
// 使用单独的模块引入 polyfills
import "babel-polyfill";
import "whatwg-fetch";

webpack.config.js

js
// 指定单独的入口
module.exports = {
  entry: {
    polyfills: "./src/polyfills.js",
  },
};

index.html

html
<script>
  // 在浏览器中添加脚本判断,如果浏览器支持则无需引入 polyfill 文件
  const modernBrowser = "fetch" in window && "assign" in Object;

  if (!modernBrowser) {
    const scriptElement = document.createElement("script");

    scriptElement.async = false;
    scriptElement.src = "/polyfills.bundle.js";
    document.head.appendChild(scriptElement);
  }
</script>