Skip to content

Webpack 配置

参考于:Webpack 配置

mode 模式

webpack 主要分为两种配置:developmentproduction

在 webpack 配置文件中指定不同的模式,来告知 webpack 使用相应模式的内置优化。

DevTool

DevTool 用于控制是否生成,以及如何生成 source map。

对于 开发环境,一般选取如下几种:

  1. eval:每个模块都使用 eval() 执行,并且都有 //# sourceURL。此选项会非常快地构建。主要缺点是,由于会映射到转换后的代码,而不是映射到原始代码(没有从 loader 中获取 source map),所以不能正确的显示行数。
  2. eval-source-map:每个模块使用 eval() 执行,并且 source map 转换为 DataUrl 后添加到 eval() 中。初始化 source map 时比较慢,但是会在重新构建时提供比较快的速度,并且生成实际的文件。行数能够正确映射,因为会映射到原始代码中。它会生成用于开发环境的最佳品质的 source map。
  3. eval-cheap-source-map:类似 eval-source-map,每个模块使用 eval() 执行。这是 "cheap(低开销)" 的 source map,因为它没有生成列映射(column mapping),只是映射行数。它会忽略源自 loader 的 source map,并且仅显示转译后的代码,就像 eval devtool。
  4. eval-cheap-module-source-map:类似 eval-cheap-source-map,并且,在这种情况下,源自 loader 的 source map 会得到更好的处理结果。然而,loader source map 会被简化为每行一个映射(mapping)。

对于 生产环境,一般选取如下几种:

  • (none)(省略 devtool 选项):不生成 source map。这是一个不错的选择。
  • source-map:整个 source map 作为一个单独的文件生成。它为 bundle 添加了一个引用注释,以便开发工具知道在哪里可以找到它。
  • hidden-source-map:与 source-map 相同,但不会为 bundle 添加引用注释。如果你只想 source map 映射那些源自错误报告的错误堆栈跟踪信息,但不想为浏览器开发工具暴露你的 source map,这个选项会很有用。
  • nosources-source-map:创建的 source map 不包含 sourcesContent(源代码内容)。它可以用来映射客户端上的堆栈跟踪,而无须暴露所有的源代码。你可以将 source map 文件部署到 web 服务器。

context 上下文

context 指的是基础目录,它是一个 绝对路径,用于从配置中解析 入口点(entry point)加载器(loader)

entry 入口

一个需要考虑的规则:每个 HTML 页面都有一个入口起点

  • 单页应用(SPA):一个入口起点。

    js
    module.exports = {
      context: resolve(__dirname, '../'),
      entry: 'src/main.js',
    }
  • 多页应用(MPA):多个入口起点。

    js
    module.exports = {
      context: resolve(__dirname),
      entry: {
        pc: './src/pages/pc/main.js',
        mobile: './src/pages/mobile/main.js',
      },
    }

关于 chunk 名称

  • 如果传入一个 字符串字符串数组,chunk 会被命名为 main。
    需要注意的是,如果是 字符串数组,会被合并成一个文件。
  • 如果传入一个对象,则每个属性的键(key)会是 chunk 的名称,该属性的值描述了 chunk 的入口点。

output 输出

output 选项指示 webpack 如何去输出、以及在哪里输出你的「bundle、asset 和其他你所打包或使用 webpack 载入的任何内容」。

publicPath

对于按需加载(on-demand-load)或加载外部资源(external resources)(如图片、文件等)来说,output.publicPath 是很重要的选项。如果指定了一个错误的值,则在加载这些资源时会收到 404 错误。

js
module.exports = {
  //...
  output: {
    // One of the below
    // 它会自动从“import.meta.url”、“document.currentScript”、“<script />”或“self.location”确定公共路径。meta.url`, `document.currentScript`, `<script />` or `self.location`.
    publicPath: 'auto',
    // CDN(总是 HTTPS 协议)
    publicPath: 'https://cdn.example.com/assets/',
    // CDN(协议相同)
    publicPath: '//cdn.example.com/assets/',
    // 相对于服务(server-relative)
    publicPath: '/assets/',
    // 相对于 HTML 页面
    publicPath: 'assets/',
    // 相对于 HTML 页面
    publicPath: '../assets/',
    // 相对于 HTML 页面(目录相同)
    publicPath: '',
  },
}

hashFunction

散列算法,webpack 默认使用的是 md4,一般我们可以使用更快速的 xxhash64 算法,来减少 CPU 消耗,并提升打包速度。

path

绝对路径,output 输出的目录。

filename

filename 决定了每个输出 bundle 的名称。这些 bundle 将写入到 output.path 选项指定的目录下。

chunkFilename

chunkFilename 选项决定了非初始(non-initial)chunk 文件的名称。

assetModuleFilename

assetModuleFilenameoutput.filename 相同,不过应用于 Asset Modules。

clean

js
module.exports = {
  //...
  output: {
    // One of the below
    // 在生成文件之前清空 output 目录
    clean: true,
    // 打印而不是删除应该移除的静态资源
    clean: {
      dry: true,
    },
    // 保留 'ignored/dir' 下的静态资源(写法1)
    clean: {
      keep: /ignored\/dir\//,
    },
    // 保留 'ignored/dir' 下的静态资源(写法2)
    clean: {
      keep(asset) {
        return asset.includes('ignored/dir')
      },
    },
  },
}

resolve 解析

resolve 选项能设置 Module 模块 如何被解析。

alias

alias 选项用于创建 import 或 require 的别名,来确保模块引入变得更简单。

js
module.exports = {
  resolve: {
    alias: {
      '@': resolve(__dirname, '../src'),
    },
  },
}

module 模块

module 选项决定了如何处理项目中的不同类型的模块。

noParse

noParse 选项可以阻止 webpack 对特定的模块进行解析和处理。常用于那些不依赖其他模块,或者我们确定不需要处理其依赖关系的库,如:jQuery、lodash。

rules

创建模块时,匹配请求的规则数组。这些规则能够修改模块的创建方式。 这些规则能够对模块(module)应用 loader,或者修改解析器(parser)。

Rule

每个 Rule 规则可以分为三部分:

  1. 条件(condition)
  2. 结果(result)
  3. 嵌套规则(nested rule)
js
module.exports = {
  module: {
    rules: [
      /* Rule */
      {
        test: /\.css$/, // 条件
        use: 'css-loader', // 结果
        rules: [
          // 嵌套规则
          {
            resourceQuery: /inline/, // 条件
            use: 'url-loader', // 结果
          },
        ],
      },
    ],
  },
}

Rule.resource

Rule.resource 选项用于匹配模块的资源路径。它可以是一个函数,一个正则表达式,一个字符串,或者这些条件的数组。

js
module.exports = {
  module: {
    rules: [
      {
        resource: /my-directory/, // 只有在 'my-directory' 目录下的模块会被匹配
        use: 'babel-loader',
      },
    ],
  },
}

Rule.resourceQuery

Rule.resourceQuery 选项用于匹配模块的查询字符串。它也可以是一个函数,一个正则表达式,一个字符串,或者这些条件的数组。

js
module.exports = {
  module: {
    rules: [
      {
        resourceQuery: /raw/, // 只有查询字符串包含 'raw' 的模块会被匹配
        use: 'raw-loader',
      },
    ],
  },
}

Rule.test

Rule.test 选项用于测试模块的路径是否符合某个条件,通常是一个正则表达式。例如,你可能会使用 Rule.test 来匹配所有 .js.css 文件,然后对这些文件应用特定的 loader。

注意:如果你提供了一个 Rule.test 选项,就不能再提供 Rule.resource

js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/, // 使用 Rule.test 匹配所有 .css 文件
        use: 'css-loader',
      },
    ],
  },
}

Rule.exclude

Rule.exclude 选项用于 排除 所有符合条件的模块。

如果你提供了 Rule.exclude 选项,就不能再提供 Rule.resource

Rule.include

Rule.include 选项用于 引入 所有符合条件的模块。

如果你提供了 Rule.include 选项,就不能再提供 Rule.resource

Rule.type

Rule.type 用于设置匹配模块的类型。可设置的值如下:

  1. javascript/auto:这是默认类型,允许在模块中混合使用 CommonJS 和 ESM。
  2. javascript/dynamic:这个类型用于处理动态导入的模块。
  3. javascript/esm:这个类型用于处理 ECMAScript 模块(ESM)。在 Webpack5 中,你可以使用 ESM 语法,并且 Webpack 会自动处理模块的导入和导出。
  4. json:这个类型用于处理 JSON 文件。Webpack 会将 JSON 文件转换为 JavaScript 对象。
  5. webassembly/sync:这个类型用于处理同步导入的 WebAssembly 模块。
  6. webassembly/async:这个类型用于处理异步导入的 WebAssembly 模块。
  7. asset:这个类型用于处理各种类型的资源文件,如图片、字体等。在 webpack 5 之前通过使用 file-loader 实现。
  8. asset/source:这个类型用于处理资源文件,Webpack 会将资源文件的源代码内联到 JavaScript bundle 中。在 webpack 5 之前通过使用 url-loader 实现。
  9. asset/resource:这个类型用于处理资源文件,Webpack 会将资源文件输出到构建目录,并在 JavaScript bundle 中包含对这些文件的 URL 引用。在 webpack 5 之前通过使用 raw-loader 实现。
  10. asset/inline:这个类型用于处理资源文件,Webpack 会将资源文件的内容作为 DataURL 内联到 JavaScript bundle 中。在 webpack 5 之前通过使用 url-loader,并且配置资源体积限制实现。

Rule.generator

Rule.generator.filenameoutput.assetModuleFilename 相同,并且仅适用于 assetasset/resource 模块类型。

Rule.use

Rule.use 选项用于指定一个或多个 loader,这些 loader 将按照 从右到左 的顺序应用于匹配的模块。

在这个例子中,所有 .css 文件都会首先被 css-loader 处理,然后再被 style-loader 处理。

js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'], // 使用 Rule.use 指定多个 loader
      },
    ],
  },
}

Rule.oneOf

Rule.oneOf 选项用于指定一组规则,其中只有第一个匹配的规则会被应用。这意味着,如果你在 oneOf 中使用 Rule.use,那么只有第一个匹配的 loader 会被应用,其他的 loader 将被忽略。

在这个例子中,对于每个模块,webpack 会首先检查它是否匹配 .css 文件。如果匹配,那么 css-loader 将被应用,然后跳过其他的规则。如果不匹配,那么 webpack 会继续检查它是否匹配 .less 文件。如果匹配,那么 less-loader 将被应用。

js
module.exports = {
  module: {
    rules: [
      {
        oneOf: [
          {
            test: /\.css$/,
            use: 'css-loader', // 使用 Rule.use 指定 loader
          },
          {
            test: /\.less$/,
            use: 'less-loader', // 使用 Rule.use 指定 loader
          },
        ],
      },
    ],
  },
}

Rule.loader

Rule.loaderRule.use: [ { loader } ] 的简写。这意味着你可以使用 Rule.loader 来更简洁地指定 loader。

这是 Rule.loader 的例子:

js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        loader: 'css-loader', // 使用 Rule.loader 指定 loader
      },
    ],
  },
}

这是相同配置的 Rule.use: [ { loader } ] 的写法:

js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: 'css-loader', // 使用 Rule.use: [ { loader } ] 指定 loader
          },
        ],
      },
    ],
  },
}

在这两个例子中,所有 .css 文件都会被 css-loader 处理。你可以看到,Rule.loader 提供了一种更简洁的方式来指定 loader,而 Rule.use: [ { loader } ] 则提供了更多的灵活性,允许你为 loader 指定选项。

Rule.options / Rule.query

Rule.optionsRule.queryRule.use: [ { options } ] 的简写。

注意:Rule.query已经被废弃,建议使用 Rule.options

这是 Rule.options 的例子:

js
module: {
  rules: [
    {
      test: /\.css$/,
      loader: 'style-loader',
      options: {
        // 这里是你的选项
      },
    },
  ]
}

在上面的例子中,options 对象中的选项将被传递给 style-loader

这是相同配置的 Rule.use: [ { options } ] 的写法:

js
module: {
  rules: [
    {
      test: /\.css$/,
      use: [
        {
          loader: 'style-loader',
          options: {
            // 这里是你的选项
          },
        },
      ],
    },
  ]
}

DevServer

http2 / https

默认情况下,开发服务器将通过 HTTP 提供服务。https2、https 需要手动开启。

headers

为所有响应添加 headers。

host

指定使用的 host。取值如下:

  • local-ip:将 local-ip 指定为主机将尝试将主机选项解析为本地 IPv4 地址(如果可用),如果 IPv4 不可用,它将尝试解析本地 IPv6 地址。
  • local-ipv4:将 local-ipv4 指定为主机将尝试将主机选项解析为本地 IPv4 地址。
  • local-ipv4:指定 local-ipv6 作为主机将尝试将主机选项解析为您的本地 IPv6 地址。

如果想让你的服务器可以被外部访问,需如下指定:

js
module.exports = {
  devServer: {
    host: '0.0.0.0',
  },
}

port

指定使用的 port 端口号。

port 配置项不能设置为 null 或者空字符串,要想自动使用一个可用端口请使用 port: 'auto'

hot vs liveReload

  • hot 是用于配置模块热替换(Hot Module Replacement)。 HMR 是指在应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个页面 ¹。当你修改了源代码文件时,webpack-dev-server 会自动重新编译并刷新浏览器,使你能够即时看到修改后的效果 ⁵。
  • liveReload 是一个在文件改变时自动刷新页面的功能。它与 hot 的区别在于,hot 只替换发生改变的模块,而 liveReload 会刷新整个页面。

为了让 liveReload 能够生效,devServer.hot 配置项必须禁用或者 devServer.watchFiles 配置项必须启用。

proxy

当拥有单独的 API 后端开发服务器并且希望在同一域上发送 API 请求时,代理某些 URL 可能会很有用。

static

static 配置项允许配置从目录提供静态文件的选项(默认是 public 文件夹)。

Stats 对象

stats 选项让你更精确地控制 bundle 信息该怎么显示。 如果你不希望使用 quiet 或 noInfo 这样的不显示信息,而是又不想得到全部的信息,只是想要获取某部分 bundle 的信息,使用 stats 选项是比较好的折衷方式。

externals 外部扩展

防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)。

例如,从 CDN 引入 jQuery,而不是把它打包:

  1. index.html

    html
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script>
  2. webpack.config.js

    js
    module.exports = {
      //...
      externals: {
        jquery: 'jQuery',
      },
    }
  3. 使用

    js
    import $ from 'jquery'
    
    $('.my-element').animate(/* ... */)

optimization 优化

从 webpack 4 开始,会根据你选择的 mode 来执行不同的优化, 不过所有的优化还是可以手动配置和重写。

splitChunks

将所有 js 文件全部引入一个 js 文件中,文件会变得很大。而且有些没有用到的模块也在一开始就加载了,使用 import 动态导入,实现 js 文件的按需加载。分成若干小的 js 文件,对于并行处理也更快。

js
splitChunks: {
  chunks: 'all' // 对所有模块都进行分割
},

RuntimeChunk

在做了缓存优化后,浏览器从第二次开始,一般就会读取缓存文件,速度也会加快,为了确保缓存不失效,所以文件在打包时,一般需要做 hash 处理,确保更新后,每次不一样,浏览器就不会出现更新异常了。但是问题来了,每次更新都不一样,那缓存不久失效了吗?所以通过 runtime 文件单独存储 hash 值,当某些文件更新,别的文件不更新时,不需要全部更新,只需要更新 runtime 内各个文件的关系就好啦。

js
runtimeChunk: {
  name: entrypoint => `runtime~${entrypoint.name}.js`
}

基于 MIT 许可发布