知海

模块方法

webpackjsorg-mainAPI 参考

模块方法

本节涵盖使用 webpack 编译的代码中可用的所有方法。使用 webpack 打包应用时,可以从多种模块语法风格中进行选择,包括 ES6CommonJSAMD

虽然 webpack 支持多种模块语法,但我们建议遵循单一语法,以保持一致性并避免奇怪的行为或错误。实际上,webpack 会对 .mjs 文件、.cjs 文件,或当最近的父级 package.json 文件包含值为 "module""commonjs""type" 字段时的 .js 文件强制执行这一建议。在继续阅读之前,请注意以下强制规则:

  • .mjspackage.json 中具有 "type": "module".js 文件
    • 不允许使用 CommonJS,例如,你不能使用 requiremodule.exportsexports
    • 导入时必须包含文件扩展名,例如,应使用 import './src/App.mjs' 而不是 import './src/App'(可以通过 Rule.resolve.fullySpecified 禁用此强制规则)
  • .cjspackage.json 中具有 "type": "commonjs".js 文件
    • importexport 均不可用
  • package.json 中具有 "type": "module".wasm 文件
    • 导入 wasm 文件时必须包含文件扩展名

ES6(推荐)

webpack 2 原生支持 ES6 模块语法,这意味着你可以直接使用 importexport,无需借助 babel 之类的工具来处理。但请记住,对于其他 ES6+ 特性,你可能仍然需要 babel。webpack 支持以下方法:

import

静态地 import 另一个模块的 export

js 复制代码
import MyModule from "./my-module.js";
import { NamedExport } from "./other-module.js";

警告: 这里的关键词是静态。普通的 import 语句不能在其他逻辑中动态使用,也不能包含变量。有关更多信息,请参阅规范;动态用法请参阅下面的 import()

你还可以 import Data URI:

js 复制代码
import "data:text/javascript;charset=utf-8;base64,Y29uc29sZS5sb2coJ2lubGluZSAxJyk7";
import {
  fn,
  number,
} from "data:text/javascript;charset=utf-8;base64,ZXhwb3J0IGNvbnN0IG51bWJlciA9IDQyOwpleHBvcnQgY29uc3QgZm4gPSAoKSA9PiAiSGVsbG8gd29ybGQiOw==";

export

将任何内容导出为 default 或命名导出(named export)。

js 复制代码
// 命名导出
export const Count = 5;
export function Multiply(a, b) {
  return a * b;
}

// 默认导出
export default {
  // 一些数据...
};

import()

function(string path):Promise

动态加载模块。调用 import() 会被视为代码分割点,也就是说,请求的模块及其子模块会被分割到单独的 chunk 中。

提示: ES2015 Loader 规范import() 定义为在运行时动态加载 ES2015 模块的方法。

js 复制代码
if (module.hot) {
  import("lodash").then((_) => {
    // 使用 lodash(也就是 '_')做点什么...
  });
}

警告: 此功能在内部依赖于 Promise。如果在旧浏览器中使用 import(),请记得使用 polyfill 来 shim Promise,例如 es6-promisepromise-polyfill

import() 中的动态表达式

不能使用完全动态的 import 语句,例如 import(foo)。因为 foo 可能指向系统或项目中的任何文件路径。

import() 必须至少包含一些关于模块位置的信息。打包可以限定在特定目录或文件集合中,这样当你使用动态表达式时,所有可能在 import() 调用中被请求的模块都会被包含进来。例如,import(`./locale/${language}.json`) 只会将 ./locale 目录及子目录中的所有 .json 文件打包到新 chunk 中,并排除其他文件扩展名的文件。在运行时,当变量 language 被计算出来后,像 english.jsongerman.json 这样的任何文件都可以使用了。

js 复制代码
// 假设我们有一种从 cookie 或其他存储获取语言的方法
const language = detectVisitorLanguage();
import(`./locale/${language}.json`).then((module) => {
  // 使用翻译内容做点什么
});

提示: 使用 webpackIncludewebpackExclude 选项,可以通过添加正则模式来减少 webpack 为此 import 打包的文件数量。

魔法注释

通过在 import 中添加注释,我们可以做很多事情,比如给 chunk 命名或选择不同的模式。有关这些魔法注释的完整列表,请参阅下面的代码,随后是这些注释作用的解释。

js 复制代码
// 单个目标
import(
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: "lazy" */
  /* webpackExports: ["default", "named"] */
  /* webpackFetchPriority: "high" */
  "node:module"
);

// 多个可能的目标
import(
  /* webpackInclude: /\.json$/ */
  /* webpackExclude: /\.noimport\.json$/ */
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: "lazy" */
  /* webpackPrefetch: true */
  /* webpackPreload: true */
  `./locale/${language}`
);

import(/* webpackIgnore: true */ "ignored-module.js");

提示: 同样支持单行注释(//)。不支持 JSDoc 注释(/** */)。

webpackIgnore

JavaScript 用法

当设置为 true 时,禁用动态导入解析。

当使用 import.meta.url 时,它不会保持原样;而是会基于 baseURI 被替换。对于模块,它会被替换为 new URL("./", import.meta.url);对于其他情况,默认使用 document.baseURI。这确保了相对 URL 能正确工作,并与基 URL 上下文保持一致。

js 复制代码
import(/* webpackIgnore: true */ "ignored-module.js");

new URL(/* webpackIgnore: true */ "./file1.css", import.meta.url);

警告: 请注意,将 webpackIgnore 设置为 true 会放弃代码分割。

CSS 用法

webpackIgnore 注释可以控制 webpack 是否处理特定的导入或 URL 引用。
它在某些情况下开箱即用,但出于性能原因,默认并不支持所有情况

我们在以下情况下支持 webpackIgnore

css 复制代码
@import /* webpackIgnore: false */ url(./basic.css);

.class {
  color: red;
  background: /* webpackIgnore: true */ url("./url/img.png");
}

.class {
  background-image: image-set(
    /*webpackIgnore:  true*/ url(./url/img1x.png) 1x,
    url(./url/img2x.png) 2x,
    url(./url/img3x.png) 3x
  );
}

提示: 对于其他 CSS 场景,css-loader 完全支持 webpackIgnore,如果需要,可以提供更大的灵活性。

HTML 用法

(自 5.107.0+ 可用)

当启用 experiments.html 时,可以将 webpackIgnore 作为 HTML 注释放在标签之前,以跳过对该标签的 srchrefsrcset 等属性的 URL 解析。该标签在生成的 HTML 中保持不变。这与 html-loader 提供的行为一致。

html 复制代码
<!-- webpackIgnore: true -->
<img src="https://cdn.example.com/logo.png" />

<!-- webpackIgnore: true -->
<script src="/legacy/external.js"></script>

该魔法注释的值使用与 JS 和 CSS 解析器相同的上下文进行解析;非布尔值会发出 UnsupportedFeatureWarning

webpackChunkName

为新 chunk 命名。自 webpack 2.6.0 起,在给定字符串中支持占位符 [index][request],它们分别会被替换为递增的数字或实际解析出的文件名。添加此注释会使我们的独立 chunk 命名为 [my-chunk-name].js,而不是 [id].js

webpackFetchPriority

(自 5.87.0+ 可用)

为特定的动态导入设置 fetchPriority。也可以使用 module.parser.javascript.dynamicImportFetchPriority 选项为所有动态导入设置全局默认值。

js 复制代码
import(
  /* webpackFetchPriority: "high" */
  "path/to/module"
);
webpackMode

自 webpack 2.6.0 起,可以指定不同的动态导入解析模式。支持以下选项:

  • 'lazy'(默认):为每个 import() 的模块生成一个可懒加载的 chunk。
  • 'lazy-once':生成一个可满足所有 import() 调用的单一懒加载 chunk。该 chunk 会在第一次调用 import() 时被获取,后续的 import() 调用将使用相同的网络响应。请注意,这仅在部分动态语句的情况下有意义,例如 import(`./locales/${language}.json`),其中可能有多个模块路径会被请求。
  • 'eager':不生成额外的 chunk。所有模块都包含在当前 chunk 中,不发起额外的网络请求。仍然会返回一个 Promise,但它已经 resolve。与静态导入相反,模块直到调用 import() 时才会执行。
  • 'weak':如果模块函数已经以某种其他方式被加载(例如,另一个 chunk 导入了它,或者加载了包含该模块的脚本),则尝试加载该模块。仍然会返回一个 Promise,但只有在该 chunk 已经在客户端上时才会成功 resolve。如果模块不可用,Promise 会被 reject。永远不会执行网络请求。这对于通用渲染很有用,因为所需的 chunk 总是在初始请求中手动提供(嵌入在页面中),但不适用于应用导航会触发未在初始请求中提供的导入的情况。
webpackPrefetch

告诉浏览器,该资源可能在未来某个导航中需要。有关 webpackPrefetch 如何工作的更多信息,请参阅指南。

webpackPreload

告诉浏览器,该资源在当前导航期间可能会需要。有关 webpackPreload 如何工作的更多信息,请参阅指南。

提示: 请注意,所有选项都可以组合使用,例如 /* webpackMode: "lazy-once", webpackChunkName: "all-i18n-data" */。这会被包装在 JavaScript 对象中,并使用 node VM 执行。你不需要添加花括号。

webpackInclude

一个正则表达式,在导入解析期间会与之进行匹配。只有匹配的模块才会被打包

webpackExclude

一个正则表达式,在导入解析期间会与之进行匹配。任何匹配的模块都不会被打包

提示: 请注意,webpackIncludewebpackExclude 选项不会干扰前缀,例如 ./locale

webpackExports

告诉 webpack 只打包动态 import() 模块的指定导出。这可以减少 chunk 的输出大小。自 webpack 5.0.0-beta.18 起可用。

警告: webpackExports 不能与解构赋值一起使用。

CommonJS

CommonJS 的目标是规定一个浏览器之外的 JavaScript 生态系统。webpack 支持以下 CommonJS 方法:

require

ts 复制代码
require(dependency: String);

同步地从另一个模块获取导出。编译器将确保该依赖在输出 bundle 中可用。

js 复制代码
import $ from "jquery";
import myModule from "my-module";

也可以为 require 启用魔法注释,更多信息请参阅 module.parser.javascript.commonjsMagicComments

警告: 异步使用它可能无法达到预期效果。

require.resolve

ts 复制代码
require.resolve(dependency: String);

同步地获取模块的 ID。编译器将确保该依赖在输出 bundle 中可用。建议将其视为不透明值,只能与 require.cache[id]__webpack_require__(id) 一起使用(最好避免这种用法)。

警告: 模块 ID 的类型可以是 numberstring,具体取决于 optimization.moduleIds 配置。

有关更多信息,请参阅 module.id

require.cache

多次 require 同一个模块只会导致一次模块执行和一次导出。因此,运行时中存在一个缓存。从该缓存中删除值会导致重新执行模块并产生新的导出。

警告: 这仅在极少数兼容性情况下才需要!

js 复制代码
import d1 from "dependency";

// 在 ESM 中,模块缓存是自动处理的。
// 不支持像 CommonJS 那样手动删除缓存。
if (import.meta.webpackHot) {
  import.meta.webpackHot.accept("dependency", (newModule) => {
    // 在这里处理模块更新
  });
}
js 复制代码
// in file.js
// 在 ESM 中,不支持手动操作缓存。
// webpack 在内部处理模块缓存。

require.ensure

警告: require.ensure() 是 webpack 特有的,并且已被 import() 取代。

ts 复制代码
require.ensure(
  dependencies: String[],
  callback: function(require),
  errorCallback: function(error),
  chunkName: String
)

将给定的 dependencies 分割到单独的 bundle 中,该 bundle 将被异步加载。当使用 CommonJS 模块语法时,这是动态加载依赖的唯一方式。也就是说,这段代码可以在执行过程中运行,只在满足特定条件时加载 dependencies

警告: 此功能在内部依赖于 Promise。如果在旧浏览器中使用 require.ensure,请记得使用 polyfill 来 shim Promise,例如 es6-promisepromise-polyfill

js 复制代码
const a = require("normal-dep");

if (module.hot) {
  import("b").then(() => {
    import("c").then((c) => {
      // 做点什么特殊的事情...
    });
  });
}

按上述顺序支持以下参数:

  • dependencies:字符串数组,声明 callback 中的代码执行所需的所有模块。
  • callback:webpack 在依赖加载完成后会执行的函数。require 函数的一个实现会作为参数发送给此函数。函数体可以使用这个参数来进一步 require() 它执行所需的模块。
  • errorCallback:当 webpack 加载依赖失败时执行的函数。
  • chunkName:由这个特定的 require.ensure() 创建的 chunk 的名称。通过向不同的 require.ensure() 调用传递相同的 chunkName,可以将它们的代码合并到单个 chunk 中,从而只产生一个浏览器必须加载的 bundle。

警告: 虽然 require 的实现作为参数传给了 callback 函数,但使用任意名称如 require.ensure([], function(request) { request('someModule'); }) 不会被 webpack 的静态解析器处理。请改用 require,例如 require.ensure([], function(require) { require('someModule'); })

AMD

警告: 这些语法是遗留的。我们强烈建议现代应用使用 ES6 模块。

异步模块定义(AMD)是一种 JavaScript 规范,它定义了编写和加载模块的接口。webpack 支持以下 AMD 方法:

define(带工厂函数)

ts 复制代码
define([name: String], [dependencies: String[]], factoryMethod: function(...))

如果提供了 dependenciesfactoryMethod 将使用每个依赖的导出(按相同顺序)来调用。如果未提供 dependenciesfactoryMethod 将使用 requireexportsmodule 来调用(为了兼容!)。如果此函数返回一个值,则该值会被模块导出。编译器会确保每个依赖都可用。

警告: 请注意,webpack 会忽略 name 参数。

js 复制代码
define(["jquery", "my-module"], ($, myModule) =>
  // 使用 $ 和 myModule 做点什么...

  // 导出一个函数
  function doSomething() {
    // ...
  });

警告: 不能在异步函数中使用。

define(带值)

ts 复制代码
define(value: !Function)

这将导出提供的 value。这里的 value 可以是除函数以外的任何内容。

js 复制代码
define({
  answer: 42,
});

警告: 不能在异步函数中使用。

require(AMD 版本)

ts 复制代码
require(dependencies: String[], [callback: function(...)])

类似于 require.ensure,这会将给定的 dependencies 分割到单独的 bundle 中,该 bundle 将被异步加载。callback 将使用 dependencies 数组中每个依赖的导出进行调用。

警告: 此功能在内部依赖于 Promise。如果在旧浏览器(例如 Internet Explorer 11)中使用 AMD,请记得使用 polyfill 来 shim Promise,例如 es6-promisepromise-polyfill

js 复制代码
import("b").then((b) => {
  import("c").then((c) => {
    // 使用 b 和 c
  });
});

警告: 无法提供 chunk 名称。

标记模块

警告: 这些语法是遗留的。我们强烈建议现代应用使用 ES6 模块。

内部的 LabeledModulesPlugin 使你能够在模块中使用以下方法进行导出和 require:

export 标签

导出给定的 value。该标签可以出现在函数声明或变量声明之前。函数名或变量名是值导出的标识符。

ts 复制代码
export: const answer = 42;
export: function method(value) {
  // 做点什么...
};

警告: 在异步函数中使用它可能无法达到预期效果。

require 标签

使依赖的所有导出在当前作用域中可用。require 标签可以出现在字符串之前。该依赖必须使用 export 标签导出值。不能使用 CommonJS 或 AMD 模块。

some-dependency.js

ts 复制代码
export: const answer = 42;
export: function method(value) {
  // 做点什么...
};
ts 复制代码
require: 'some-dependency';
console.log(answer);
method(...);

Webpack

除了上述模块语法外,webpack 还允许一些自定义的、webpack 特有的方法:

require.context

ts 复制代码
require.context(
  (directory: String),
  (includeSubdirs: Boolean) /* 可选,默认为 true */,
  (filter: RegExp) /* 可选,默认为 /^\.\/.*$/,任何文件 */,
  (mode: String) /* 可选,'sync' | 'eager' | 'weak' | 'lazy' | 'lazy-once',默认 'sync' */
);

使用指向 directory 的路径、includeSubdirs 选项、用于更精细控制所包含模块的 filter 以及定义加载方式的 mode,可以指定一整组依赖。底层模块随后可以通过以下方式解析:

js 复制代码
const context = import.meta.webpackContext("components", {
  recursive: true,
  regExp: /\.html$/,
});
const componentA = context.resolve("componentA");

如果 mode 设置为 'lazy',底层模块将被异步加载:

js 复制代码
const context = import.meta.webpackContext("locales", {
  recursive: true,
  regExp: /\.json$/,
  mode: "lazy",
});
context("localeA").then((locale) => {
  // 使用 locale 做点什么
});

可用模式的完整列表及其行为在 import() 文档中描述。

require.include

ts 复制代码
require.include((dependency: String));

包含一个 dependency,但不执行它。这可以用于优化模块在输出 chunk 中的位置。

js 复制代码
import("a");
import("b");
import("c").then((moduleC) => {
  // 在这里使用 moduleC
});

这将产生以下输出:

  • 入口 chunk:file.jsa
  • 匿名 chunk:b
  • 匿名 chunk:c

如果没有 require.include('a')a 会在这两个匿名 chunk 中重复出现。

require.resolveWeak

类似于 require.resolve,但这不会将 module 拉入 bundle。这就是所谓的“弱”依赖。

js 复制代码
if (__webpack_modules__[require.resolveWeak("module")]) {
  // 当模块可用时做点什么...
}
if (require.cache[require.resolveWeak("module")]) {
  // 当模块之前已加载时做点什么...
}

// 你可以进行动态解析(“context”),
// 类似于其他 require/import 方法。
const page = "Foo";
__webpack_modules__[require.resolveWeak(`./page/${page}`)];

提示: require.resolveWeak 是_通用渲染_(SSR + 代码分割)的基础,如 react-universal-component 等包中所使用的那样。它允许代码在服务器端和客户端初始页面加载时同步渲染。它要求 chunk 被手动提供或以某种方式可用。它能够 require 模块,而不表示它们应该被打包到 chunk 中。它与 import() 结合使用,当用户导航触发额外的导入时代替它。

warning

如果模块源码包含无法静态分析的 require,则会发出关键依赖(critical dependencies)警告。

示例代码:

js 复制代码
someFn(require);
require.bind(null);
require(variable);

帮助我们改进文档

发现翻译问题或内容错误?请告诉我们。