本章節(jié)涵蓋了使用 webpack 編譯代碼的所有方法。在 webpack 打包應(yīng)用程序時(shí),你可以選擇各種模塊語法風(fēng)格,包括 ES6,CommonJS 和 AMD。
盡管 webpack 支持多種模塊語法,但我們還是建議盡量使用一致的語法,以此避免一些奇怪的行為和 bug。事實(shí)上,當(dāng)距離最近的 package.json 文件中包含值為 "module" 或 "commonjs" 的 "type" 字段時(shí),webpack 會(huì)啟用語法一致性檢查。請(qǐng)大家在閱讀前,注意此情況:
webpack 2 支持原生的 ES6 模塊語法,意味著你無須額外引入 babel 這樣的工具,就可以使用 import 和 export。但是注意,如果使用其他的 ES6+ 特性,仍然需要引入 babel。webpack 支持以下的方法:
通過 import 以靜態(tài)的方式導(dǎo)入另一個(gè)通過 export 導(dǎo)出的模塊。
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==';
默認(rèn)導(dǎo)出整個(gè)模塊或具名導(dǎo)出模塊。
// 具名導(dǎo)出
export var Count = 5;
export function Multiply(a, b) {
return a * b;
}
// 默認(rèn)導(dǎo)出
export default {
// Some data...
};
function(string path):Promise
動(dòng)態(tài)的加載模塊。調(diào)用 import 的之處,被視為分割點(diǎn),意思是,被請(qǐng)求的模塊和它引用的所有子模塊,會(huì)分割到一個(gè)單獨(dú)的 chunk 中。
if (module.hot) {
import('lodash').then((_) => {
// Do something with lodash (a.k.a '_')...
});
}
不能使用完全動(dòng)態(tài)的 import 語句,例如 import(foo)。是因?yàn)?nbsp;foo 可能是系統(tǒng)或項(xiàng)目中任何文件的任何路徑。
import() 必須至少包含一些關(guān)于模塊的路徑信息。打包可以限定于一個(gè)特定的目錄或文件集,以便于在使用動(dòng)態(tài)表達(dá)式時(shí) - 包括可能在 import() 調(diào)用中請(qǐng)求的每個(gè)模塊。例如, import(`./locale/${language}.json`) 會(huì)把 .locale 目錄中的每個(gè) .json 文件打包到新的 chunk 中。在運(yùn)行時(shí),計(jì)算完變量 language 后,就可以使用像 english.json 或 german.json 的任何文件。
// 想象我們有一個(gè)從 cookies 或其他存儲(chǔ)中獲取語言的方法
const language = detectVisitorLanguage();
import(`./locale/${language}.json`).then((module) => {
// do something with the translations
});
內(nèi)聯(lián)注釋使這一特性得以實(shí)現(xiàn)。通過在 import 中添加注釋,我們可以進(jìn)行諸如給 chunk 命名或選擇不同模式的操作。
// 單個(gè)目標(biāo)
import(
/* webpackChunkName: "my-chunk-name" */
/* webpackMode: "lazy" */
/* webpackExports: ["default", "named"] */
'module'
);
// 多個(gè)可能的目標(biāo)
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:設(shè)置為 true 時(shí),禁用動(dòng)態(tài)導(dǎo)入解析。
webpackChunkName: 新 chunk 的名稱。 從 webpack 2.6.0 開始,占位符 [index] 和 [request] 分別支持遞增的數(shù)字或?qū)嶋H的解析文件名。 添加此注釋后,將單獨(dú)的給我們的 chunk 命名為 [my-chunk-name].js 而不是 [id].js。
webpackMode:從 webpack 2.6.0 開始,可以指定以不同的模式解析動(dòng)態(tài)導(dǎo)入。支持以下選項(xiàng):
webpackPrefetch:告訴瀏覽器將來可能需要該資源來進(jìn)行某些導(dǎo)航跳轉(zhuǎn)。
webpackPreload:告訴瀏覽器在當(dāng)前導(dǎo)航期間可能需要該資源。
webpackInclude:在導(dǎo)入解析(import resolution)過程中,用于匹配的正則表達(dá)式。只有匹配到的模塊才會(huì)被打包。
webpackExclude:在導(dǎo)入解析(import resolution)過程中,用于匹配的正則表達(dá)式。所有匹配到的模塊都不會(huì)被打包。
webpackExports: 告知 webpack 只構(gòu)建指定出口的動(dòng)態(tài) import() 模塊。它可以減小 chunk 的大小。從 webpack 5.0.0-beta.18 起可用。
CommonJS 的目標(biāo)是為瀏覽器之外的 JavaScript 指定一個(gè)生態(tài)系統(tǒng)。webpack 支持以下 CommonJS 的方法:
require(dependency: String);
以同步的方式檢索其他模塊的導(dǎo)出。編譯器(compiler)會(huì)確保依賴項(xiàng)在輸出 bundle 中可用。
var $ = require('jquery');
var myModule = require('my-module');
也可以為 require 啟用魔法注釋。
require.resolve(dependency: String);
以同步的方式獲取模塊的 ID。編譯器(compiler)會(huì)確保依賴項(xiàng)在最終輸出 bundle 中可用。建議將其視為不透明值,只能與 require.cache[id] 或 __webpack_require__(id) 配合使用(最好避免這種用法)。
有關(guān)更多模塊的信息,詳見 module.id。
多處引用同一模塊,最終只會(huì)產(chǎn)生一次模塊執(zhí)行和一次導(dǎo)出。所以,會(huì)在運(yùn)行時(shí)(runtime)中會(huì)保存一份緩存。刪除此緩存,則會(huì)產(chǎn)生新的模塊執(zhí)行和新的導(dǎo)出。
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; // 理論上是不相等的;實(shí)際運(yùn)行中,則會(huì)導(dǎo)致堆棧溢出
require.cache[module.id] !== module;
require.ensure(
dependencies: String[],
callback: function(require),
errorCallback: function(error),
chunkName: String
)
給定 dependencies 參數(shù),將其對(duì)應(yīng)的文件拆分到一個(gè)單獨(dú)的 bundle 中,此 bundle 會(huì)被異步加載。當(dāng)使用 CommonJS 模塊語法時(shí),這是動(dòng)態(tài)加載依賴項(xiàng)的唯一方法。這意味著,可以在模塊執(zhí)行時(shí)才允許代碼,只有在滿足特定條件時(shí)才會(huì)加載 dependencies。
var a = require('normal-dep');
if (module.hot) {
require.ensure(['b'], function (require) {
var c = require('c');
// Do something special...
});
}
按照上面指定的順序,require.ensure 支持以下參數(shù):
AMD(Asynchronous Module Definition)是一種定義了用于編寫和加載模塊接口的 JavaScript 規(guī)范。
define([name: String], [dependencies: String[]], factoryMethod: function(...))
如果提供了 dependencies 參數(shù),就會(huì)調(diào)用 factoryMethod 方法,并(以相同的順序)導(dǎo)出每個(gè)依賴項(xiàng)。如果未提供 dependencies 參數(shù),調(diào)用 factoryMethod 方法時(shí)會(huì)傳入 require , exports 和 module(用于兼容?。H绻朔椒ǚ祷匾粋€(gè)值,則返回值會(huì)作為此模塊的導(dǎo)出。由編譯器(compiler)來確保依賴項(xiàng)在最終輸出的 bundle 中可用。
define(['jquery', 'my-module'], function ($, myModule) {
// 使用 $ 和 myModule 做一些操作...
// 導(dǎo)出一個(gè)函數(shù)
return function doSomething() {
// ...
};
});
define(value: !Function)
這種方式只將提供的 value 導(dǎo)出。這里的 value 可以是除函數(shù)以外的任何值。
define({
answer: 42,
});
require(dependencies: String[], [callback: function(...)])
與 require.ensure 類似,給定 dependencies 參數(shù),將其對(duì)應(yīng)的文件拆分到一個(gè)單獨(dú)的 bundle 中,此 bundle 會(huì)被異步加載。然后會(huì)調(diào)用 callback 回調(diào)函數(shù),并傳入 dependencies 數(shù)組中的每個(gè)項(xiàng)導(dǎo)出。
require(['b'], function (b) {
var c = require('c');
});
webpack 內(nèi)置的 LabeledModulesPlugin 插件,允許你使用下面的方法導(dǎo)出和導(dǎo)入模塊:
導(dǎo)出給定的 value。標(biāo)簽可以出現(xiàn)在函數(shù)聲明或變量聲明之前。函數(shù)名或變量名是導(dǎo)出值的標(biāo)識(shí)符。
export: var answer = 42;
export: function method(value) {
// Do something...
};
在當(dāng)前作用域下,依賴項(xiàng)的所有導(dǎo)出均可用。require 標(biāo)簽可以放置在一個(gè)字符串之前。依賴模塊必須使用 export 標(biāo)簽導(dǎo)出值。CommonJS 或 AMD 模塊無法通過這種方式使用。
some-dependency.js
export: var answer = 42;
export: function method(value) {
// Do something...
};
require: 'some-dependency';
console.log(answer);
method(...);
除了上述模塊語法之外,還允許使用一些 webpack 特定的方法:
require.context(
(directory: String),
(includeSubdirs: Boolean) /* 可選的,默認(rèn)值是 true */,
(filter: RegExp) /* 可選的,默認(rèn)值是 /^\.\/.*$/,所有文件 */,
(mode: String) /* 可選的, 'sync' | 'eager' | 'weak' | 'lazy' | 'lazy-once',默認(rèn)值是 'sync' */
)
指定一系列依賴項(xiàng),通過使用 directory 的路徑,以及 includeSubdirs ,filter 選項(xiàng),進(jìn)行更細(xì)粒度的模塊引入,使用 mode 定義加載方式。以此可以很容易的解析模塊:
var context = require.context('components', true, /\.html$/);
var componentA = context.resolve('componentA');
如果 mode 設(shè)置為 lazy,基礎(chǔ)模塊將以異步方式加載:
var context = require.context('locales', true, /\.json$/, 'lazy');
context('localeA').then((locale) => {
// do something with locale
});
mode 的可用模式及說明的完整列表在 import() 文檔 中進(jìn)行了描述。
require.include((dependency: String));
引入一個(gè)不需要執(zhí)行的 dependency,這樣可以用于優(yōu)化輸出 chunk 中依賴模塊的位置。
require.include('a');
require.ensure(['a', 'b'], function (require) {
/* ... */
});
require.ensure(['a', 'c'], function (require) {
/* ... */
});
這會(huì)產(chǎn)生以下輸出:
不使用 require.include('a'),輸出的兩個(gè)匿名 chunk 都會(huì)有模塊 a。
與 require.resolve 類似,但是不會(huì)把 module 引入到 bundle 中。這就是所謂的“弱(weak)”依賴。
if (__webpack_modules__[require.resolveWeak('module')]) {
// 當(dāng)模塊可用時(shí),執(zhí)行一些操作……
}
if (require.cache[require.resolveWeak('module')]) {
// 在模塊加載完成之前,執(zhí)行一些操作……
}
// 你可以像執(zhí)行其他 require/import 方法一樣,
// 執(zhí)行動(dòng)態(tài)解析上下文 resolves ("context")。
const page = 'Foo';
__webpack_modules__[require.resolveWeak(`./page/${page}`)];
require.resolveWeak
是_通用渲染_(服務(wù)器端渲染 SSR + 代碼分割 Code Splitting)的基礎(chǔ)。例如在 react-universal-component 等包中的用法。它允許代碼在服務(wù)器端和客戶端初始頁面的加載上同步渲染。它要求手動(dòng)或以某種方式提供 chunk。它可以在不需要指示應(yīng)該被打包的情況下引入模塊。它與import()
一起使用,當(dāng)用戶導(dǎo)航觸發(fā)額外的導(dǎo)入時(shí),它會(huì)被接管。
warning:
如果模塊源碼包含無法靜態(tài)分析的 require,則會(huì)發(fā)出關(guān)鍵依賴項(xiàng)警告。
示例代碼:
someFn(require);
require.bind(null);
require(variable);
更多建議: