模块方法
模块方法
本节涵盖使用 webpack 编译的代码中可用的所有方法。使用 webpack 打包应用时,可以从多种模块语法风格中进行选择,包括 ES6、CommonJS 和 AMD。
虽然 webpack 支持多种模块语法,但我们建议遵循单一语法,以保持一致性并避免奇怪的行为或错误。实际上,webpack 会对 .mjs 文件、.cjs 文件,或当最近的父级 package.json 文件包含值为 "module" 或 "commonjs" 的 "type" 字段时的 .js 文件强制执行这一建议。在继续阅读之前,请注意以下强制规则:
.mjs或package.json中具有"type": "module"的.js文件- 不允许使用 CommonJS,例如,你不能使用
require、module.exports或exports - 导入时必须包含文件扩展名,例如,应使用
import './src/App.mjs'而不是import './src/App'(可以通过Rule.resolve.fullySpecified禁用此强制规则)
- 不允许使用 CommonJS,例如,你不能使用
.cjs或package.json中具有"type": "commonjs"的.js文件import和export均不可用
package.json中具有"type": "module"的.wasm文件- 导入 wasm 文件时必须包含文件扩展名
ES6(推荐)
webpack 2 原生支持 ES6 模块语法,这意味着你可以直接使用 import 和 export,无需借助 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 来 shimPromise,例如 es6-promise 或 promise-polyfill。
import() 中的动态表达式
不能使用完全动态的 import 语句,例如 import(foo)。因为 foo 可能指向系统或项目中的任何文件路径。
import() 必须至少包含一些关于模块位置的信息。打包可以限定在特定目录或文件集合中,这样当你使用动态表达式时,所有可能在 import() 调用中被请求的模块都会被包含进来。例如,import(`./locale/${language}.json`) 只会将 ./locale 目录及子目录中的所有 .json 文件打包到新 chunk 中,并排除其他文件扩展名的文件。在运行时,当变量 language 被计算出来后,像 english.json 或 german.json 这样的任何文件都可以使用了。
js
// 假设我们有一种从 cookie 或其他存储获取语言的方法
const language = detectVisitorLanguage();
import(`./locale/${language}.json`).then((module) => {
// 使用翻译内容做点什么
});
提示: 使用
webpackInclude和webpackExclude选项,可以通过添加正则模式来减少 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 注释放在标签之前,以跳过对该标签的 src、href、srcset 等属性的 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
一个正则表达式,在导入解析期间会与之进行匹配。任何匹配的模块都不会被打包。
提示: 请注意,
webpackInclude和webpackExclude选项不会干扰前缀,例如./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 的类型可以是
number或string,具体取决于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 来 shimPromise,例如 es6-promise 或 promise-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(...))
如果提供了 dependencies,factoryMethod 将使用每个依赖的导出(按相同顺序)来调用。如果未提供 dependencies,factoryMethod 将使用 require、exports 和 module 来调用(为了兼容!)。如果此函数返回一个值,则该值会被模块导出。编译器会确保每个依赖都可用。
警告: 请注意,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 来 shimPromise,例如 es6-promise 或 promise-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.js和a - 匿名 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);
帮助我们改进文档
发现翻译问题或内容错误?请告诉我们。
