从 webpack 3 迁移
本指南介绍了从 webpack 1 迁移到 webpack 2 的主要变化。
T> 需要注意的是,从 webpack 2 到 webpack 3 的变化要小得多,因此迁移过程应该不会太复杂。如果遇到问题,请参阅更新日志了解详情。
resolve.root、resolve.fallback、resolve.modulesDirectories
这些选项已被 resolve.modules 这一个选项取代。更多用法请参见解析。
diff
resolve: {
- root: path.join(__dirname, "src")
+ modules: [
+ path.join(__dirname, "src"),
+ "node_modules"
+ ]
}
resolve.extensions
此选项不再需要传入空字符串。此行为已移至 resolve.enforceExtension。更多用法请参见解析。
resolve.*
这里的一些 API 发生了变化。由于使用不广泛,此处不再详细列出。详情请参见解析。
module.loaders 现在是 module.rules
旧的 loader 配置已被更强大的 rules 系统取代,该系统允许配置 loader 及更多内容。
出于兼容性考虑,旧的 module.loaders 语法仍然有效,并且旧的名称仍会被解析。
新的命名约定更易于理解,也是升级配置以使用 module.rules 的一个很好的理由。
diff
module: {
- loaders: [
+ rules: [
{
test: /\.css$/,
- loaders: [
- "style-loader",
- "css-loader?modules=true"
+ use: [
+ {
+ loader: "style-loader"
+ },
+ {
+ loader: "css-loader",
+ options: {
+ modules: true
+ }
+ }
]
},
{
test: /\.jsx$/,
loader: "babel-loader", // 此处不要使用 "use"
options: {
// ...
}
}
]
}
链式 loader
与 webpack 1 类似,loader 可以链式调用,以将结果从一个 loader 传递给下一个 loader。通过 rule.use 配置选项,use 可以设置为一个 loader 数组。
在 webpack 1 中,loader 通常使用 ! 进行链式调用。这种风格仅在旧的 module.loaders 选项下受支持。
diff
module: {
- loaders: [{
+ rules: [{
test: /\.less$/,
- loader: "style-loader!css-loader!less-loader"
+ use: [
+ "style-loader",
+ "css-loader",
+ "less-loader"
+ ]
}]
}
自动添加 -loader 模块名扩展已被移除
引用 loader 时,不能再省略 -loader 扩展名:
diff
module: {
rules: [
{
use: [
- "style",
+ "style-loader",
- "css",
+ "css-loader",
- "less",
+ "less-loader",
]
}
]
}
你仍然可以通过 resolveLoader.moduleExtensions 配置选项选择旧行为,但不推荐这样做。
diff
+ resolveLoader: {
+ moduleExtensions: ["-loader"]
+ }
此变更的原因请参见 #2986。
不再需要 json-loader
当没有为 JSON 文件配置 loader 时,webpack 将自动尝试使用 json-loader 加载 JSON 文件。
diff
module: {
rules: [
- {
- test: /\.json/,
- loader: "json-loader"
- }
]
}
我们决定这样做是为了消除 webpack、node.js 和 browserify 之间的环境差异。
配置中的 loader 相对于 context 解析
在 webpack 1 中,配置的 loader 相对于匹配的文件进行解析。然而,在 webpack 2 中,配置的 loader 相对于 context 选项进行解析。
这解决了在使用 npm link 或引用 context 外部模块时,由 loader 引起的重复模块问题。
你可以移除一些用于绕过此问题的 hack:
diff
module: {
rules: [
{
// ...
- loader: require.resolve("my-loader")
+ loader: "my-loader"
}
]
},
resolveLoader: {
- root: path.resolve(__dirname, "node_modules")
}
module.preLoaders 和 module.postLoaders 已被移除
diff
module: {
- preLoaders: [
+ rules: [
{
test: /\.js$/,
+ enforce: "pre",
loader: "eslint-loader"
}
]
}
UglifyJsPlugin sourceMap
UglifyJsPlugin 的 sourceMap 选项现在默认为 false,而不是 true。这意味着,如果你对压缩后的代码使用 source map,或者希望为 uglifyjs 警告提供正确的行号,则需要为 UglifyJsPlugin 设置 sourceMap: true。
diff
devtool: "source-map",
plugins: [
new UglifyJsPlugin({
+ sourceMap: true
})
]
UglifyJsPlugin 警告
UglifyJsPlugin 的 compress.warnings 选项现在默认为 false,而不是 true。
这意味着,如果你想看到 uglifyjs 警告,则需要将 compress.warnings 设置为 true。
diff
devtool: "source-map",
plugins: [
new UglifyJsPlugin({
+ compress: {
+ warnings: true
+ }
})
]
UglifyJsPlugin 压缩 loaders
UglifyJsPlugin 不再将 loader 切换到压缩模式。从长远来看,需要通过 loader 选项传递 minimize: true 设置。有关相关选项,请参阅 loader 文档。
loader 的压缩模式将在 webpack 3 或更高版本中移除。
为了保持与旧 loaders 的兼容性,可以通过插件将 loaders 切换到压缩模式:
diff
plugins: [
+ new webpack.LoaderOptionsPlugin({
+ minimize: true
+ })
]
DedupePlugin 已被移除
webpack.optimize.DedupePlugin 不再是必需的。请将其从配置中移除。
BannerPlugin - 重大变更
BannerPlugin 不再接受两个参数,而是接受一个单独的 options 对象。
diff
plugins: [
- new webpack.BannerPlugin('Banner', {raw: true, entryOnly: true});
+ new webpack.BannerPlugin({banner: 'Banner', raw: true, entryOnly: true});
]
OccurrenceOrderPlugin 现在默认启用
OccurrenceOrderPlugin 现在默认启用,并且已被重命名(webpack 1 中为 OccurenceOrderPlugin)。
因此,请确保从配置中移除该插件:
diff
plugins: [
// webpack 1
- new webpack.optimize.OccurenceOrderPlugin()
// webpack 2
- new webpack.optimize.OccurrenceOrderPlugin()
]
ExtractTextWebpackPlugin - 重大变更
ExtractTextPlugin 需要版本 2 才能与 webpack 2 配合使用。
npm install --save-dev extract-text-webpack-plugin
此插件的配置更改主要是语法上的。
ExtractTextPlugin.extract
diff
module: {
rules: [
{
test: /.css$/,
- loader: ExtractTextPlugin.extract("style-loader", "css-loader", { publicPath: "/dist" })
+ use: ExtractTextPlugin.extract({
+ fallback: "style-loader",
+ use: "css-loader",
+ publicPath: "/dist"
+ })
}
]
}
new ExtractTextPlugin({options})
diff
plugins: [
- new ExtractTextPlugin("bundle.css", { allChunks: true, disable: false })
+ new ExtractTextPlugin({
+ filename: "bundle.css",
+ disable: false,
+ allChunks: true
+ })
]
完全动态的 require 现在默认失败
仅包含表达式的依赖(例如 require(expr))现在将创建一个空的 context,而不是整个目录的 context。
这样的代码应该被重构,因为它将无法与 ES2015 模块一起工作。如果无法重构,你可以使用 ContextReplacementPlugin 来提示编译器进行正确的解析。
?> 链接到一篇关于动态依赖的文章。
在 CLI 和配置中使用自定义参数
如果你滥用 CLI 向配置传递自定义参数,如下所示:
webpack --custom-stuff
js
// webpack.config.js
const customStuff = process.argv.includes("--custom-stuff");
/* ... */
module.exports = config;
你可能会注意到这不再被允许。CLI 现在更加严格。
相反,现在有一个接口用于向配置传递参数。你应该使用这个接口。未来的工具可能会依赖于此。
webpack --env.customStuff
js
module.exports = function (env) {
const { customStuff } = env;
/* ... */
return config;
};
请参见 CLI。
require.ensure 和 AMD require 是异步的
这些函数现在始终是异步的,而不会在 chunk 已加载时同步调用其回调。
require.ensure 现在依赖于原生的 Promise。如果在缺少 Promise 的环境中使用 require.ensure,则需要提供一个 polyfill。
Loader 配置通过 options 进行
你 不再可以 在 webpack.config.js 中使用自定义属性来配置 loader。它必须通过 options 来完成。以下带有 ts 属性的配置在 webpack 2 中不再有效:
js
module.exports = {
// ...
module: {
rules: [
{
test: /\.tsx?$/,
loader: "ts-loader",
},
],
},
// 不适用于 webpack 2
ts: { transpileOnly: false },
};
什么是 options?
问得好。严格来说,它可能指的是两件事;都是配置 webpack loader 的方式。传统上,options 被称为 query,是一个可以附加到 loader 名称后面的字符串。很像查询字符串,但实际上具有更强大的功能:
js
module.exports = {
// ...
module: {
rules: [
{
test: /\.tsx?$/,
loader: `ts-loader?${JSON.stringify({ transpileOnly: false })}`,
},
],
},
};
但它也可以是一个单独指定的对象,与 loader 一起提供:
js
module.exports = {
// ...
module: {
rules: [
{
test: /\.tsx?$/,
loader: "ts-loader",
options: { transpileOnly: false },
},
],
},
};
LoaderOptionsPlugin context
一些 loader 需要上下文信息并从配置中读取它们。从长远来看,这需要通过 loader 选项传递。有关相关选项,请参阅 loader 文档。
为了保持与旧 loaders 的兼容性,可以通过插件传递此信息:
diff
plugins: [
+ new webpack.LoaderOptionsPlugin({
+ options: {
+ context: __dirname
+ }
+ })
]
debug
debug 选项在 webpack 1 中将 loader 切换到调试模式。从长远来看,这需要通过 loader 选项传递。有关相关选项,请参阅 loader 文档。
loader 的调试模式将在 webpack 3 或更高版本中移除。
为了保持与旧 loaders 的兼容性,可以通过插件将 loaders 切换到调试模式:
diff
- debug: true,
plugins: [
+ new webpack.LoaderOptionsPlugin({
+ debug: true
+ })
]
使用 ES2015 进行代码分割
在 webpack 1 中,你可以使用 require.ensure() 作为为应用程序懒加载 chunk 的方法:
js
require.ensure([], (require) => {
const foo = require("./module");
});
ES2015 Loader 规范定义了 import() 作为在运行时动态加载 ES2015 模块的方法。Webpack 将 import() 视为一个分割点,并将请求的模块放入一个单独的 chunk 中。import() 接受模块名作为参数并返回一个 Promise。
js
function onClick() {
import("./module")
.then((module) => module.default)
.catch((err) => {
console.log("Chunk loading failed");
});
}
好消息:由于 chunk 加载是基于 Promise 的,现在可以处理加载失败的情况了。
动态表达式
可以向 import() 传递一个部分表达式。这类似于 CommonJS 中表达式的处理方式(webpack 会为所有可能的文件创建一个 context)。
import() 会为每个可能的模块创建一个单独的 chunk。
js
function route(path, query) {
return import(`./routes/${path}/route`).then(
(route) => new route.Route(query),
);
}
// 这会为每个可能的路由创建一个单独的 chunk
混合使用 ES2015、AMD 和 CommonJS
至于 AMD 和 CommonJS,你可以自由地混合使用这三种模块类型(甚至可以在同一个文件中)。在这种情况下,Webpack 的行为类似于 babel 和 node-eps:
js
// CommonJS 消费 ES2015 模块
const book = require("./book");
book.currentPage;
book.readPage();
book.default === "This is a book";
js
// ES2015 模块消费 CommonJS
import fs from "node:fs"; // module.exports 映射到 default
typeof fs.readFileSync === "function";
js
// ES2015 模块消费 CommonJS
import { readFileSync } from "node:fs"; // 命名导出从返回的对象中读取+
typeof readFileSync === "function";
需要注意的是,你需要告诉 Babel 不要解析这些模块符号,以便 webpack 可以使用它们。你可以通过在 .babelrc 或 babel-loader 选项中设置以下内容来实现。
.babelrc
json
{
"presets": [["es2015", { "modules": false }]]
}
提示
无需更改,但提供了一些机会
模板字符串
Webpack 现在支持在表达式中使用模板字符串。这意味着你可以在 webpack 构造中开始使用它们:
diff
- require("./templates/" + name);
+ require(`./templates/${name}`);
配置 Promise
Webpack 现在支持从配置文件返回一个 Promise。这允许在配置文件中进行异步处理。
webpack.config.js
js
module.exports = function () {
return fetchLangs().then((lang) => ({
entry: "...",
// ...
plugins: [new DefinePlugin({ LANGUAGE: lang })],
}));
};
高级 loader 匹配
Webpack 现在支持更多用于匹配 loader 的方式。
js
module.exports = {
// ...
module: {
rules: [
{
resource: /filename/, // 匹配 "/path/filename.js"
resourceQuery: /^\?querystring$/, // 匹配 "?querystring"
issuer: /filename/, // 如果从 "/path/filename.js" 请求,则匹配 "/path/something.js"
},
],
},
};
更多 CLI 选项
有一些新的 CLI 选项供你使用:
--define process.env.NODE_ENV="production" 请参见 DefinePlugin。
--display-depth 显示每个模块到入口点的距离。
--display-used-exports 显示关于模块中使用了哪些导出信息。
--display-max-modules 设置输出中显示的模块数量(默认为 15)。
-p 现在也会将 process.env.NODE_ENV 定义为 "production"。
Loader 变更
仅与 loader 作者相关的变更。
Cacheable
Loaders 现在默认是可缓存的。如果 loader 不可缓存,则必须选择退出。
diff
// 可缓存的 loader
module.exports = function(source) {
- this.cacheable();
return source;
}
diff
// 不可缓存的 loader
module.exports = function(source) {
+ this.cacheable(false);
return source;
}
复杂选项
webpack 1 仅支持可通过 JSON.stringify 序列化的 loader 选项。
webpack 2 现在支持任何 JS 对象作为 loader 选项。
在 webpack 2.2.1 之前(即从 2.0.0 到 2.2.0),使用复杂选项需要为 options 对象使用 ident,以便其他 loader 可以引用它。这在 2.2.1 中已被移除,因此当前的迁移不需要使用任何 ident 键。
diff
{
test: /\.ext/
use: {
loader: '...',
options: {
- ident: 'id',
fn: () => require('./foo.js')
}
}
}
帮助我们改进文档
发现翻译问题或内容错误?请告诉我们。
