模块方法

本节涵盖了在 webpack 编译的代码中可用的所有方法。使用 webpack 打包应用程序时,您可以选择多种模块语法样式,包括 ES6CommonJSAMD

尽管 webpack 支持多种模块语法,但我们建议为了一致性并避免异常行为/错误,只遵循一种语法。实际上,当 `.mjs` 文件、`.cjs` 文件或 `.js` 文件最近的父级 `package.json` 文件包含值为 `"module"` 或 `"commonjs"` 的 `"type"` 字段时,webpack 会强制执行此建议。请在阅读下文之前注意这些强制规定。

  • 在 `package.json` 中,使用 `"type": "module"` 的 `.mjs` 或 `.js` 文件
    • 不允许使用 CommonJS,例如,你不能使用 `require`、`module.exports` 或 `exports`
    • 导入时需要文件扩展名,例如,你应该使用 `import './src/App.mjs'` 而不是 `import './src/App'`(你可以通过 `Rule.resolve.fullySpecified` 禁用此强制规定)
  • 在 `package.json` 中,使用 `"type": "commonjs"` 的 `.cjs` 或 `.js` 文件
    • 既不能使用 `import` 也不能使用 `export`
  • 在 `package.json` 中,使用 `"type": "module"` 的 `.wasm` 文件
    • 导入 wasm 文件时需要文件扩展名

ES6(推荐)

webpack 的第 2 版原生支持 ES6 模块语法,这意味着你可以直接使用 `import` 和 `export`,而无需像 Babel 这样的工具来处理。请记住,你可能仍然需要 Babel 来处理其他 ES6+ 特性。webpack 支持以下方法:

import

静态地 `import` 另一个模块的 `export`。

import MyModule from './my-module.js';
import { NamedExport } from './other-module.js';

你也可以 `import` Data URI

import 'data:text/javascript;charset=utf-8;base64,Y29uc29sZS5sb2coJ2lubGluZSAxJyk7';
import {
  number,
  fn,
} from 'data:text/javascript;charset=utf-8;base64,ZXhwb3J0IGNvbnN0IG51bWJlciA9IDQyOwpleHBvcnQgY29uc3QgZm4gPSAoKSA9PiAiSGVsbG8gd29ybGQiOw==';

export

将任何内容作为 `default` 或命名导出。

// Named exports
export var Count = 5;
export function Multiply(a, b) {
  return a * b;
}

// Default export
export default {
  // Some data...
};

import()

function(string path):Promise

动态加载模块。`import()` 调用被视为分割点,这意味着请求的模块及其子模块将被分割成一个单独的 chunk。

if (module.hot) {
  import('lodash').then((_) => {
    // Do something with lodash (a.k.a '_')...
  });
}

import() 中的动态表达式

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

`import()` 必须至少包含一些关于模块位置的信息。打包可以限制在特定的目录或文件集内,这样当你使用动态表达式时——在 `import()` 调用中可能被请求的每个模块都会被包含进来。例如,`import(`./locale/${language}.json`)` 将导致 `./locale` 目录中的所有 `.json` 文件被打包到一个新的 chunk 中。在运行时,当变量 `language` 被计算出来后,任何文件(如 `english.json` 或 `german.json`)都将可供使用。

// imagine we had a method to get language from cookies or other storage
const language = detectVisitorLanguage();
import(`./locale/${language}.json`).then((module) => {
  // do something with the translations
});

魔法注释

内联注释可使功能生效。通过在 import 中添加注释,我们可以做一些事情,例如命名我们的 chunk 或选择不同的模式。有关这些魔法注释的完整列表,请参阅下面的代码,然后是这些注释功能的解释。

// Single target
import(
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: "lazy" */
  /* webpackExports: ["default", "named"] */
  /* webpackFetchPriority: "high" */
  'module'
);

// Multiple possible targets
import(
  /* webpackInclude: /\.json$/ */
  /* webpackExclude: /\.noimport\.json$/ */
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: "lazy" */
  /* webpackPrefetch: true */
  /* webpackPreload: true */
  `./locale/${language}`
);

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

JavaScript 用法

当设置为 `true` 时,禁用动态 import 解析。

当使用 `import.meta.url` 时,它不会保持原样;相反,它会根据 `baseURI` 进行替换。对于模块,它被替换为 `new URL("./", import.meta.url)`,对于其他情况,它默认为 `document.baseURI`。这确保了相对 URL 正常工作,与基本 URL 上下文对齐。

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

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

CSS 用法

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

我们在以下情况支持 `webpackIgnore`

@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
  );
}
webpackChunkName

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

webpackFetchPriority
5.87.0+

为特定的动态 import 设置 `fetchPriority`。也可以通过使用 `module.parser.javascript.dynamicImportFetchPriority` 选项为所有动态 import 设置一个全局默认值。

import(
  /* webpackFetchPriority: "high" */
  'path/to/module'
);
webpackMode

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

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

告诉浏览器将来某个导航可能需要此资源。查看指南以获取有关 webpackPrefetch 如何工作的更多信息。

webpackPreload

告诉浏览器当前导航可能需要此资源。查看指南以获取有关 webpackPreload 如何工作的更多信息。

webpackInclude

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

webpackExclude

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

webpackExports

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

CommonJS

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

require

require(dependency: String);

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

var $ = require('jquery');
var myModule = require('my-module');

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

require.resolve

require.resolve(dependency: String);

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

请参阅 `module.id` 获取更多信息。

require.cache

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

var d1 = require('dependency');
require('dependency') === d1;
delete require.cache[require.resolve('dependency')];
require('dependency') !== d1;
// in file.js
require.cache[module.id] === module;
require('./file.js') === module.exports;
delete require.cache[module.id];
require.cache[module.id] === undefined;
require('./file.js') !== module.exports; // in theory; in praxis this causes a stack overflow
require.cache[module.id] !== module;

require.ensure

require.ensure(
  dependencies: String[],
  callback: function(require),
  errorCallback: function(error),
  chunkName: String
)

将给定的 `dependencies` 分割成一个单独的 bundle,该 bundle 将异步加载。当使用 CommonJS 模块语法时,这是动态加载依赖项的唯一方法。这意味着,此代码可以在执行期间运行,仅当满足特定条件时才加载 `dependencies`。

var a = require('normal-dep');

if (module.hot) {
  require.ensure(['b'], function (require) {
    var c = require('c');

    // Do something special...
  });
}

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

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

AMD

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

define(带工厂函数)

define([name: String], [dependencies: String[]], factoryMethod: function(...))

如果提供了 `dependencies`,`factoryMethod` 将会以每个依赖项的导出(按相同顺序)被调用。如果没有提供 `dependencies`,`factoryMethod` 将以 `require`、`exports` 和 `module` 被调用(为了兼容性!)。如果此函数返回一个值,则该值由模块导出。编译器确保每个依赖项都可用。

define(['jquery', 'my-module'], function ($, myModule) {
  // Do something with $ and myModule...

  // Export a function
  return function doSomething() {
    // ...
  };
});

define(带值)

define(value: !Function)

这将导出提供的值。这里的值可以是除函数以外的任何东西。

define({
  answer: 42,
});

require (AMD 版本)

require(dependencies: String[], [callback: function(...)])

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

require(['b'], function (b) {
  var c = require('c');
});

带标签的模块

内部的 `LabeledModulesPlugin` 允许你在模块中使用以下导出和 require 方法:

export label

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

export: var answer = 42;
export: function method(value) {
  // Do something...
};

require label

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

some-dependency.js

export: var answer = 42;
export: function method(value) {
  // Do something...
};
require: 'some-dependency';
console.log(answer);
method(...);

Webpack

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

require.context

require.context(
  (directory: String),
  (includeSubdirs: Boolean) /* optional, default true */,
  (filter: RegExp) /* optional, default /^\.\/.*$/, any file */,
  (mode: String) /* optional, 'sync' | 'eager' | 'weak' | 'lazy' | 'lazy-once', default 'sync' */
);

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

var context = require.context('components', true, /\.html$/);
var componentA = context.resolve('componentA');

如果 `mode` 设置为 `'lazy'`,则底层模块将异步加载。

var context = require.context('locales', true, /\.json$/, 'lazy');
context('localeA').then((locale) => {
  // do something with locale
});

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

require.include

require.include((dependency: String));

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

require.include('a');
require.ensure(['a', 'b'], function (require) {
  /* ... */
});
require.ensure(['a', 'c'], function (require) {
  /* ... */
});

这将产生以下输出:

  • 入口 chunk:`file.js` 和 `a`
  • 匿名 chunk:`b`
  • 匿名 chunk:`c`

如果没有 `require.include('a')`,它将在两个匿名 chunk 中重复。

require.resolveWeak

类似于 `require.resolve`,但它不会将 `module` 拉入 bundle。这被认为是一种“弱”依赖。

if (__webpack_modules__[require.resolveWeak('module')]) {
  // Do something when module is available...
}
if (require.cache[require.resolveWeak('module')]) {
  // Do something when module was loaded before...
}

// You can perform dynamic resolves ("context")
// similarly to other require/import methods.
const page = 'Foo';
__webpack_modules__[require.resolveWeak(`./page/${page}`)];

警告

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

示例代码

someFn(require);
require.bind(null);
require(variable);

11 贡献者

skipjacksokrafadysamirsadekbyzykdebs-obrienwizardofhogwartsEugeneHlushkochenxsanjamesgeorge007WofWcasnitin315