- 0133技术站
- 联系QQ:18840023
- QQ交流群

- 微信公众号

以下各节描述从 webpack 1 到 webpack 2 的重大变化。
T> webpack 从 1 到 2 的变化,比从 2 到 3 要少很多,所以版本迁移起来难度应该不大。如果你遇到了问题,请查看 更新日志 以了解更多细节。
W> 随着 webpack 2 版本已经发布一段时间,此章节的内容可能会在不久的将来被转移到博客文章中。最重要的是,webpack 3 版本最近发布,webpack 4 版本即将发布。如上所述,大家最好是参考 更新日志 去进行相应的版本迁移。
resolve.root
, resolve.fallback
, resolve.modulesDirectories
这些选项被一个单独的选项 resolve.modules
取代。更多用法请查看解析。
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
的好理由。
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: { // ... } } ] }
就像在 webpack 1 中,loader 可以链式调用,上一个 loader 的输出被作为输入传给下一个 loader。
使用 rule.use 配置选项,use
可以设置为一个 loader 数组。
在 webpack 1 中,loader 通常被用 !
连写。这一写法在 webpack 2 中只在使用旧的选项 module.loaders
时才有效。
module: { - loaders: [{ + rules: [{ test: /\.less$/, - loader: "style-loader!css-loader!less-loader" + use: [ + "style-loader", + "css-loader", + "less-loader" + ] }] }
-loader
后缀」在引用 loader 时,不能再省略 -loader
后缀了:
module: { rules: [ { use: [ - "style", + "style-loader", - "css", + "css-loader", - "less", + "less-loader", ] } ] }
你仍然可以通过配置 resolveLoader.moduleExtensions
配置选项,启用这一旧有行为,但是我们不推荐这么做。
+ resolveLoader: { + moduleExtensions: ["-loader"] + }
了解这一改变背后的原因,请查看 #2986。
json-loader
不再需要手动添加如果没有为 JSON 文件配置 loader,webpack 将自动尝试通过 json-loader
加载 JSON 文件。
module: { rules: [ - { - test: /\.json/, - loader: "json-loader" - } ] }
我们决定这么做是为了消除 webpack、 node.js 和 browserify 之间的环境差异。
在 webpack 1 中,默认配置下 loader 解析相对于被匹配的文件。然而,在 webpack 2 中,默认配置下 loader 解析相对于 context
选项。
这解决了「在使用 npm link
或引用 context
上下文目录之外的模块时,loader 所导致的模块重复载入」的问题。
你可以移除掉那些为解决此问题的 hack 方案了:
module: { rules: [ { // ... - loader: require.resolve("my-loader") + loader: "my-loader" } ] }, resolveLoader: { - root: path.resolve(__dirname, "node_modules") }
module.preLoaders
和 module.postLoaders
module: { - preLoaders: [ + rules: [ { test: /\.js$/, + enforce: "pre", loader: "eslint-loader" } ] }
UglifyJsPlugin
sourceMapUglifyJsPlugin
的 sourceMap
选项现在默认为 false
而不是 true
。这意味着如果你在压缩代码时启用了 source map,或者想要让 uglifyjs 的警告能够对应到正确的代码行,你需要将 UglifyJsPlugin
的 sourceMap
设为 true
。
devtool: "source-map", plugins: [ new UglifyJsPlugin({ + sourceMap: true }) ]
UglifyJsPlugin
warningsUglifyJsPlugin
的 compress.warnings
选项现在默认为 false
而不是 true
。
这意味着如果你想要看到 uglifyjs 的警告信息,你需要将 compress.warnings
设为 true
。
devtool: "source-map", plugins: [ new UglifyJsPlugin({ + compress: { + warnings: true + } }) ]
UglifyJsPlugin
压缩 loadersUglifyJsPlugin
不再压缩 loaders。在未来很长一段时间里,需要通过设置 minimize:true
来压缩 loaders。参考 loader 文档里的相关选项。
loaders 的压缩模式将在 webpack 3 或后续版本中取消。
为了兼容旧的 loaders,loaders 可以通过插件来切换到压缩模式:
plugins: [ + new webpack.LoaderOptionsPlugin({ + minimize: true + }) ]
DedupePlugin
不再需要 webpack.optimize.DedupePlugin
。请从配置中移除。
BannerPlugin
- 破坏性改动BannerPlugin
不再接受两个参数,而是只接受单独的 options 对象。
plugins: [ - new webpack.BannerPlugin('Banner', {raw: true, entryOnly: true}); + new webpack.BannerPlugin({banner: 'Banner', raw: true, entryOnly: true}); ]
OccurrenceOrderPlugin
OccurrenceOrderPlugin
现在默认启用,并已重命名(在 webpack 1 中为 OccurenceOrderPlugin
)。
因此,请确保从你的配置中删除该插件:
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
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})
plugins: [ - new ExtractTextPlugin("bundle.css", { allChunks: true, disable: false }) + new ExtractTextPlugin({ + filename: "bundle.css", + disable: false, + allChunks: true + })]
只有一个表达式的依赖(例如 require(expr)
)将创建一个空的 context 而不是一个完整目录的 context。
这样的代码应该进行重构,因为它不能与 ES2015 模块一起使用。如果你确定不会有 ES2015 模块,你可以使用 ContextReplacementPlugin
来指示 compiler 进行正确的解析。
?> Link to an article about dynamic dependencies.
如果你之前滥用 CLI 来传自定义参数到配置中,比如:
webpack --custom-stuff
// webpack.config.js var customStuff = process.argv.indexOf("--custom-stuff") >= 0; /* ... */ module.exports = config;
你将会发现新版中不再允许这么做。CLI 现在更加严格了。
替代地,现在提供了一个接口来传递参数给配置。我们应该采用这种新方式,在未来许多工具将可能依赖于此。
webpack --env.customStuff
module.exports = function(env) { var customStuff = env.customStuff; /* ... */ return config; };
详见 CLI。
require.ensure
以及 AMD require
将采用异步式调用现在这些函数总是异步的,而不是当 chunk 已经加载完成的时候同步调用它们的回调函数(callback)。
require.ensure
现在依赖于原生的 Promise
。如果在不支持 Promise 的环境里使用 require.ensure
,你需要添加 polyfill。
options
配置 loader你不能再通过 webpack.config.js
的自定义属性来配置 loader。只能通过 options
来配置。下面配置的 ts
属性在 webpack 2 下不再有效:
module.exports = { ... module: { rules: [{ test: /\.tsx?$/, loader: 'ts-loader' }] }, // 在 webpack 2 中无效 ts: { transpileOnly: false } }
options
?好问题。严格来说,有两种办法,都可以用来配置 webpack 的 loader。典型的 options
被称为 query
,是一个可以被添加到 loader 名之后的字符串。它比较像一个 query string,但是实际上有更强大的能力:
module.exports = { ... module: { rules: [{ test: /\.tsx?$/, loader: 'ts-loader?' + JSON.stringify({ transpileOnly: false }) }] } }
不过它也可以分开来,写成一个单独的对象,紧跟在 loader 属性后面:
module.exports = { ... module: { rules: [{ test: /\.tsx?$/, loader: 'ts-loader', options: { transpileOnly: false } }] } }
LoaderOptionsPlugin
context有的 loader 需要从配置中读取一些 context 信息。在未来很长一段时间里,这将需要通过 loader options 传入。详见 loader 文档的相关选项。
为了保持对旧 loaders 的兼容,这些信息可以通过插件传进来:
plugins: [ + new webpack.LoaderOptionsPlugin({ + options: { + context: __dirname+ } + }) ]
debug
在 webpack 1 中 debug
选项可以将 loader 切换到调试模式(debug mode)。在未来很长一段时间里,这将需要通过 loader 选项传递。详见 loader 文档的相关选项。
loaders 的调试模式将在 webpack 3 或后续版本中取消。
为了保持对旧 loaders 的兼容,loader 可以通过插件来切换到调试模式:
- debug: true, plugins: [ + new webpack.LoaderOptionsPlugin({ + debug: true + }) ]
在 webpack 1 中,可以使用 require.ensure
作为实现应用程序的懒加载 chunks 的一种方法:
require.ensure([], function(require) { var foo = require("./module"); });
ES2015 模块加载规范定义了 import()
方法,可以在运行时(runtime)动态地加载 ES2015 模块。webpack 将 import()
作为分割点(split-point)并将所要请求的模块(requested module)放置到一个单独的 chunk 中。import()
接收模块名作为参数,并返回一个 Promise。
function onClick() { import("./module").then(module => { return module.default; }).catch(err => { console.log("Chunk loading failed"); }); }
好消息是:如果加载 chunk 失败,我们现在可以进行处理,因为现在它基于 Promise
。
可以传递部分表达式给 import()
。这与 CommonJS 对表达式的处理方式一致(webpack 为所有可能匹配的文件创建 context)。
import()
为每一个可能的模块创建独立的 chunk。
function route(path, query) { return import(`./routes/${path}/route`) .then(route => new route.Route(query)); } // 上面代码为每个可能的路由创建独立的 chunk
你可以自由混合使用三种模块类型(甚至在同一个文件中)。在这个情况中 webpack 的行为和 babel 以及 node-eps 一致:
// CommonJS 调用 ES2015 模块 var book = require("./book"); book.currentPage; book.readPage(); book.default === "This is a book";
// ES2015 模块调用 CommonJS import fs from "fs"; // module.exports 映射到 default import { readFileSync } from "fs"; // 从返回对象(returned object+)中读取命名的导出方法(named exports) typeof fs.readFileSync === "function"; typeof readFileSync === "function";
值得注意的是,你需要让 Babel 不解析这些模块符号,从而让 webpack 可以使用它们。你可以通过设置如下配置到 .babelrc 或 babel-loader 来实现这一点。
.babelrc
{ "presets": [ ["es2015", { "modules": false }] ] }
不需要改变什么,但有机会改变。
webpack 现在支持表达式中的模板字符串了。这意味着你可以在 webpack 构建中使用它们:
- require("./templates/" + name);+ require(`./templates/${name}`);
webpack 现在支持在配置文件中返回 Promise
了。这让你能在配置文件中做异步处理。
webpack.config.js
module.exports = function() { return fetchLangs().then(lang => ({ entry: "...", // ... plugins: [ new DefinePlugin({ LANGUAGE: lang }) ] })); };
webpack 现在支持对 loader 进行更多方式的匹配。
module: { rules: [ { resource: /filename/, // 匹配 "/path/filename.js" resourceQuery: /^\?querystring$/, // 匹配 "?querystring" issuer: /filename/, // 如果请求 "/path/filename.js" 则匹配 "/path/something.js" } ] }
你可以使用一些新的 CLI 参数项:
--define process.env.NODE_ENV="production"
见 DefinePlugin
。
--display-depth
显示每个模块到入口的距离。
--display-used-exports
显示一个模块中被使用的 exports 信息。
--display-max-modules
设置输出时显示的模块数量(默认是 15)。
-p
能够定义 process.env.NODE_ENV
为 "production"
。
以下变更仅影响 loader 的开发者。
Loaders 现在默认可被缓存。Loaders 如果不想被缓存,需要选择不被缓存。
// 缓存 loader module.exports = function(source) { - this.cacheable(); return source; }
// 不缓存 loader module.exports = function(source) { + this.cacheable(false); return source; }
webpack 1 只支持能够「可 JSON.stringify
的对象」作为 loader 的 options。
webpack 2 现在支持任意 JS 对象作为 loader 的 options.
webpack 2.2.1之前(即从 2.0.0 到 2.2.0),使用复合 options,需要在 options
对象上添加 ident
,允许它能够被其他 loader 引用。这在 2.2.1 中被删除,因此目前的迁移不再需要使用 ident
键。
{ test: /\.ext/ use: { loader: '...', options: { - ident: 'id', fn: () => require('./foo.js') } } }
推荐手册