- webpack概述
- 入口起点(Entry Points)
- 输出(Output)
- 模块(Mode)
- 加载器(Loaders)
- 插件(Plugins)
- 配置(Configuration)
- 模块(Modules)
- 模块解析(Module Resolution)
- 依赖图表(Dependency Graph)
- 文件清单(Manifest)
- 构建目标(Targets)
- 模块热替换(Hot Module Replacement)
- 第二部分:配置
- 使用不同语言进行配置(Configuration Languages)
- 多种配置类型
- 入口和上下文(Entry and Context)
- 输出(Output)
- 模块(Module)
- 解析(Resolve)
- 插件(Plugins)
- 开发中 Server(DevServer)
- 开发工具(Devtool)
- 构建目标(Targets)
- Watch 和 WatchOptions
- 外部扩展(Externals)
- 性能(Performance)
- Node
- 统计(Stats)
- 其它选项(Other Options)
- 第三部分:API
- 命令行接口(CLI)
- 包含统计数据的文件(stats data)
- Node.js API
- 模块热替换(Hot Module Replacement)
- 加载器 API
- 模块方法(module methods)
- 模块变量(module variables)
- Plugin API
- compiler 钩子
- compilation 钩子
- resolver
- parser
- 第四部分:指南
- 安装
- 起步
- 管理资源
- 管理输出
- 开发
- 模块热替换
- Tree shaking
- 生产环境构建
- 代码拆分(Code Splitting)
- 懒加载(Lazy Loading)
- 缓存(Caching)
- 创建库 (Library)
- Shimming
- 渐进式网络应用程序
- TypeScript
- 迁移到新版本
- 使用环境变量
- 构建性能
- 内容安全策略
- 开发 - Vagrant
- 管理依赖
- Public Path(公共路径)
- 集成(Integrations)
- 第五部分:加载器
- babel-loader
- yaml-frontmatter-loader
- cache-loader
- coffee-loader
- coffee-redux-loader
- coverjs-loader
- css-loader
- exports-loader
- expose-loader
- extract-loader
- file-loader
- gzip-loader
- html-loader
- i18n-loader
- imports-loader
- istanbul-instrumenter-loader
- jshint-loader
- json-loader
- json5-loader
- less-loader
- bundle-loader
- multi-loader
- node-loader
- null-loader
- polymer-webpack-loader
- postcss-loader
- raw-loader
- react-proxy-loader
- restyle-loader
- sass-loader
- script-loader
- source-map-loader
- style-loader
- svg-inline-loader
- thread-loader
- transform-loader
- url-loader
- val-loader
- worker-loader
- mocha-loader
- 第六部分:插件
- AggressiveSplittingPlugin
- ZopfliWebpackPlugin
- BannerPlugin
- ClosureWebpackPlugin
- CommonsChunkPlugin
- ComponentWebpackPlugin
- CompressionWebpackPlugin
- ContextReplacementPlugin
- CopyWebpackPlugin
- DefinePlugin
- DllPlugin
- EnvironmentPlugin
- EvalSourceMapDevToolPlugin
- ExtractTextWebpackPlugin
- HashedModuleIdsPlugin
- HotModuleReplacementPlugin
- HtmlWebpackPlugin
- BabelMinifyWebpackPlugin
- IgnorePlugin
- LoaderOptionsPlugin
- MinChunkSizePlugin
- ModuleConcatenationPlugin
- NamedModulesPlugin
- NormalModuleReplacementPlugin
- NpmInstallWebpackPlugin
- PrefetchPlugin
- ProfilingPlugin
- ProvidePlugin
- SourceMapDevToolPlugin
- SplitChunksPlugin
- UglifyjsWebpackPlugin
- WatchIgnorePlugin
- I18nWebpackPlugin
模块方法(module methods)
本节涵盖了使用 webpack 编译代码的所有方法。在 webpack 打包应用程序时,你可以选择各种模块语法风格,包括 ES6, CommonJS 和 AMD。
W> 虽然 webpack 支持多种模块语法,但我们建议尽量遵循一致的语法,避免一些奇怪的行为和 bug。这是一个混合使用了 ES6 和 CommonJS 的示例,但我们确定还有其他的 BUG 会产生。
ES6(推荐)
webpack 2 支持原生的 ES6 模块语法,意味着你可以无须额外引入 babel 这样的工具,就可以使用 import
和 export
。但是注意,如果使用其他的 ES6+ 特性,仍然需要引入 babel。webpack 支持以下的方法:
import
通过 import
以静态的方式,导入另一个通过 export
导出的模块。
import MyModule from './my-module.js';
import { NamedExport } from './other-module.js';
W> 这里的关键词是静态的。标准的 import
语句中,模块语句中不能以「具有逻辑或含有变量」的动态方式去引入其他模块。关于 import 的更多信息和 import()
动态用法,请查看这里的说明。
export
默认
导出整个模块,或具名导出模块
// 具名导出
export var Count = 5;
export function Multiply(a, b) {
return a * b;
}
// 默认导出
export default {
// Some data...
}
import()
import('path/to/module') -> Promise
动态地加载模块。调用 import()
之处,被作为分离的模块起点,意思是,被请求的模块和它引用的所有子模块,会分离到一个单独的 chunk 中。
T> ES2015 loader 规范 定义了 import()
方法,可以在运行时动态地加载 ES2015 模块。
if ( module.hot ) {
import('lodash').then(_ => {
// Do something with lodash (a.k.a '_')...
})
}
W> import() 特性依赖于内置的 Promise
。如果想在低版本浏览器使用 import(),记得使用像 es6-promise 或者 promise-polyfill 这样 polyfill 库,来预先填充(shim) Promise
环境。
import
规范不允许控制模块的名称或其他属性,因为 "chunks" 只是 webpack 中的一个概念。幸运的是,webpack 中可以通过注释接收一些特殊的参数,而无须破坏规定:
import(
/* webpackChunkName: "my-chunk-name" */
/* webpackMode: "lazy" */
'module'
);
webpackChunkName
:新 chunk 的名称。从 webpack 2.6.0 开始,[index]
and [request]
占位符,分别支持赋予一个递增的数字和实际解析的文件名。
webpackMode
:从 webpack 2.6.0 开始,可以指定以不同的模式解析动态导入。支持以下选项:
"lazy"
(默认):为每个import()
导入的模块,生成一个可延迟加载(lazy-loadable) chunk。"lazy-once"
:生成一个可以满足所有import()
调用的单个可延迟加载(lazy-loadable) chunk。此 chunk 将在第一次import()
调用时获取,随后的import()
调用将使用相同的网络响应。注意,这种模式仅在部分动态语句中有意义,例如import(`./locales/${language}.json`)
,其中可能含有多个被请求的模块路径。"eager"
:不会生成额外的 chunk,所有模块都被当前 chunk 引入,并且没有额外的网络请求。仍然会返回Promise
,但是是 resolved 状态。和静态导入相对比,在调用 import()完成之前,该模块不会被执行。"weak"
:尝试加载模块,如果该模块函数已经以其他方式加载(即,另一个 chunk 导入过此模块,或包含模块的脚本被加载)。仍然会返回Promise
,但是只有在客户端上已经有该 chunk 时才成功解析。如果该模块不可用,Promise
将会是 rejected 状态,并且网络请求永远不会执行。当需要的 chunks 始终在(嵌入在页面中的)初始请求中手动提供,而不是在应用程序导航在最初没有提供的模块导入的情况触发,这对于通用渲染(SSR)是非常有用的。
T> 请注意,这两个选项可以组合起来使用,如 /* webpackMode: "lazy-once", webpackChunkName: "all-i18n-data" */
,这会按没有花括号的 JSON5 对象去解析。
W> 完全动态的语句(如 import(foo)
),因为 webpack 至少需要一些文件的路径信息,而 foo
可能是系统或项目中任何文件的任何路径,因此 foo
将会解析失败。import()
必须至少包含模块位于何处的路径信息,所以打包应当限制在一个指定目录或一组文件中。
W> 调用 import()
时,包含在其中的动态表达式 request,会潜在的请求的每个模块。例如,import(`./locale/${language}.json`)
会导致 ./locale
目录下的每个 .json
文件,都被打包到新的 chunk 中。在运行时,当计算出变量 language
时,任何文件(如 english.json
或 german.json
)都可能会被用到。
W> 在 webpack 中使用 System.import
不符合提案规范,所以在2.1.0-beta.28 后被弃用,并且建议使用 import()
。
CommonJS
CommonJS 致力于为浏览器之外的 JavaScript 指定一个生态系统。webpack 支持以下的 CommonJS 方法:
require
require(dependency: String)
以同步的方式检索其他模块的导出。由编译器(compiler)来确保依赖项在最终输出 bundle 中可用。
var $ = require("jquery");
var myModule = require("my-module");
W> 以异步的方式使用,可能不会达到预期的效果。
require.resolve
require.resolve(dependency: String)
以同步的方式获取模块的 ID。由编译器(compiler)来确保依赖项在最终输出 bundle 中可用。更多关于模块的信息,请点击这里 module.id
。
W> webpack 中模块 ID 是一个数字(而在 NodeJS 中是一个字符串 -- 也就是文件名)。
require.cache
多处引用同一个模块,最终只会产生一次模块执行和一次导出。所以,会在运行时(runtime)中会保存一份缓存。删除此缓存,会产生新的模块执行和新的导出。
W> 只有很少数的情况需要考虑兼容性!
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 // 这是理论上的操作不相等;在实际运行中,会导致栈溢出
require.cache[module.id] !== module
require.ensure
W> require.ensure()
是 webpack 特有的,已经被 import()
取代。
require.ensure(dependencies: String[], callback: function(require), errorCallback: function(error), chunkName: String)
给定 dependencies
参数,将其对应的文件拆分到一个单独的 bundle 中,此 bundle 会被异步加载。当使用 CommonJS 模块语法时,这是动态加载依赖的唯一方法。意味着,可以在模块执行时才运行代码,只有在满足某些条件时才加载依赖项
。
W> 这个特性依赖于内置的 Promise
。如果想在低版本浏览器使用 require.ensure
,记得使用像 es6-promise 或者 promise-polyfill 这样 polyfill 库,来预先填充(shim) Promise
环境。
var a = require('normal-dep');
if ( module.hot ) {
require.ensure(['b'], function(require) {
var c = require('c');
// Do something special...
});
}
按照上面指定的顺序,webpack 支持以下参数:
dependencies
:字符串构成的数组,声明callback
回调函数中所需的所有模块。callback
:只要加载好全部依赖,webpack 就会执行此函数。require
函数的实现,作为参数传入此函数。当程序运行需要依赖时,可以使用require()
来加载依赖。函数体可以使用此参数,来进一步执行require()
模块。errorCallback
:当 webpack 加载依赖失败时,会执行此函数。chunkName
:由require.ensure()
创建出的 chunk 的名字。通过将同一个chunkName
传递给不同的require.ensure()
调用,我们可以将它们的代码合并到一个单独的 chunk 中,从而只产生一个浏览器必须加载的 bundle。
W> 虽然我们将 require
的实现,作为参数传递给回调函数,然而如果使用随意的名字,例如 require.ensure([], function(request) { request('someModule'); })
则无法被 webpack 静态解析器处理,所以还是请使用 require
,例如 require.ensure([], function(require) { require('someModule'); })
。
AMD
AMD(Asynchronous Module Definition) 是一种定义了写入模块接口和加载模块接口的 JavaScript 规范。webpack 支持以下的 AMD 方法:
define
(通过 factory 方法导出)
define([name: String], [dependencies: String[]], factoryMethod: function(...))
如果提供 dependencies
参数,将会调用 factoryMethod
方法,并(以相同的顺序)传入每个依赖项的导出。如果未提供 dependencies
参数,则调用 factoryMethod
方法时传入 require
, exports
和 module
(用于兼容)。如果此方法返回一个值,则返回值会作为此模块的导出。由编译器(compiler)来确保依赖项在最终输出 bundle 中可用。
W> 注意:webpack 会忽略 name
参数。
define(['jquery', 'my-module'], function($, myModule) {
// 使用 $ 和 myModule 做一些操作……
// 导出一个函数
return function doSomething() {
// ...
};
});
W> 此 define 导出方式不能在异步函数中调用。
define
(通过 value 导出)
define(value: !Function)
只会将提供的 value
导出。这里的 value
可以是除函数外的任何值。
define({
answer: 42
});
W> 此 define 导出方式不能在异步函数中调用。
require
(AMD 版本)
require(dependencies: String[], [callback: function(...)])
与 require.ensure
类似,给定 dependencies
参数,将其对应的文件拆分到一个单独的 bundle 中,此 bundle 会被异步加载。然后会调用 callback
回调函数,并传入 dependencies
数组中每一项的导出。
W> 这个特性依赖于内置的 Promise
。如果想在低版本浏览器使用 require.ensure
,记得使用像 es6-promise 或者 promise-polyfill 这样 polyfill 库,来预先填充(shim) Promise
环境。
require(['b'], function(b) {
var c = require("c");
});
W> 这里没有提供命名 chunk 名称的选项。
标签模块(Labeled Modules)
webpack 内置的 LabeledModulesPlugin
插件,允许使用下面的方法导出和导入模块:
export
标签
导出给定的 value
。export
标记可以出现在函数声明或变量声明之前。函数名或变量名是导出值的标识符。
export: var answer = 42;
export: function method(value) {
// 做一些操作……
};
W> 以异步的方式使用,可能不会达到预期的效果。
require
标签
使当前作用域下,可访问所依赖模块的所有导出。require
标签可以放置在一个字符串之前。依赖模块必须使用 export
标签导出值。CommonJS 或 AMD 模块无法通过这种方式,使用标签模块的导出。
some-dependency.js
export: var answer = 42;
export: function method(value) {
// 执行一些操作……
};
require: 'some-dependency';
console.log(answer);
method(...);
webpack
webpack 除了支持上述的语法之外,还可以使用一些 webpack 特定的方法:
require.context
require.context(directory:String, includeSubdirs:Boolean /* 可选的,默认值是 true */, filter:RegExp /* 可选的 */)
使用 directory
路径、includeSubdirs
选项和 filter
来指定一系列完整的依赖关系,便于更细粒度的控制模块引入。后面可以很容易地进行解析:
var context = require.context('components', true, /\.html$/);
var componentA = context.resolve('componentA');
require.include
require.include(dependency: String)
引入一个不需要执行的依赖
,这可以用于优化输出 chunk 中的依赖模块的位置。
require.include('a');
require.ensure(['a', 'b'], function(require) { /* ... */ });
require.ensure(['a', 'c'], function(require) { /* ... */ });
这会产生以下输出:
- entry chunk:
file.js
anda
- anonymous chunk:
b
- anonymous chunk:
c
如果不使用 require.include('a')
,输出的两个匿名 chunk 都有模块 a。
require.resolveWeak
与 require.resolve
类似,但是这不会将 module
引入到 bundle 中。这就是所谓的"弱(weak)"依赖。
if(__webpack_modules__[require.resolveWeak('module')]) {
// 模块可用时,执行一些操作……
}
if(require.cache[require.resolveWeak('module')]) {
// 在模块被加载之前,执行一些操作……
}
// 你可以像执行其他 require/import 方法一样,
// 执行动态解析(“上下文”)。
const page = 'Foo';
__webpack_modules__[require.resolveWeak(`./page/${page}`)]
T> require.resolveWeak
是通用渲染(SSR + 代码分离)的基础,例如在 react-universal-component 等包中的用法。它允许代码在服务器端和客户端初始页面的加载上同步渲染。它要求手动或以某种方式提供 chunk。它可以在不需要指示应该被打包的情况下引入模块。它与 import()
一起使用,当用户导航触发额外的导入时,它会被接管。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论