Work in progress... This reflects stuff until 2.0.5-beta+
webpack 2 brings native support ES6 Modules. This means webpack now understands import
and export
without them being transformed to CommonJS:
import { currentPage, readPage } from "./book";
currentPage === 0;
readPage();
currentPage === 1;
// book.js
export var currentPage = 0;
export function readPage() {
currentPage++;
}
export default "This is a book";
The ES6 Loader spec defines System.import
as method to load ES6 Modules dynamically on runtime.
Webpack threads System.import
as splitpoint and puts the requested module in a separate chunk.
System.import
takes the module name as argument and returns a Promise.
function onClick() {
System.import("./module").then(module => {
module.default;
}).catch(err => {
console.log("Chunk loading failed");
});
}
Good news: Failure to load a chunk can be handled now.
It's possible to pass an partial expression to System.import
. This is handled similar to expressions in CommonJS (webpack creates a context with all possible files).
System.import
creates a separate chunk for each possible module.
function route(path, query) {
return System.import("./routes/" + path + "/route")
.then(route => new route.Route(query));
}
// This creates a separate chunk for each possible route
As for AMD and CommonJS you can freely mix all three module types (even within the same file). Webpack behaves similar to babel in this case:
// CommonJS consuming ES6 Module
var book = require("./book");
book.currentPage;
book.readPage();
book.default === "This is a book";
// ES6 Module consuming CommonJS
import fs from "fs"; // module.exports map to default
import { readFileSync } from "fs"; // named exports are read from returned object+
typeof fs.readFileSync === "function";
typeof readFileSync === "function";
The es2015
babel preset transforms ES6 Modules to CommonJS by default. To use webpack to process ES6 Modules you should use the es2015-webpack
preset instead.
The static nature of ES6 Modules allows some new kind of optimizations. In example in many cases it's possible to detect which exports are used and which aren't used.
In cases in which webpack can say for sure that an export isn't used it omits the statement which exposes the export to other modules. Later the minimizer may flag the declaration as unused and omits it.
In the following cases it's possible to detect usage:
- named import
- default import
- reexport
In the following cases it's not possible to detect usage:
import * as ...
when used indirectly- CommonJS or AMD consuming ES6 module
System.import
In cases where it's possible to track export usage, webpack can mangle export names to single char properties.
In the past environment variables are often used to handle different environments in the configuration file. Webpack 2 brings a new way to pass options to the configuration.
The configuration file can export a function which returns the configuration. The function is called by the CLI and the value passed via --env
is passed to the configuration function.
You can pass a string (--env dev
=> "dev"
) or a complex options object (--env.minimize --env.server localhost
=> {minimize: true, server: "localhost"}
). I would recommend using an object, because it's more extendable, but it's up to you.
// webpack.config.babel.js
exports default function(options) {
return {
// ...
devtool: options.dev ? "cheap-module-eval-source-map" : "hidden-source-map"
};
}
There was a major refactoring in the resolver (https://github.com/webpack/enhanced-resolve). This means the resolving option were changed too. Mostly simplification and changes that make it more unlikely to configure it incorrectly.
The new options are:
{
modules: [path.resolve(__dirname, "app"), "node_modules"]
// (was split into `root`, `modulesDirectories` and `fallback` in the old options)
// In which folders the resolver look for modules
// relative paths are looked up in every parent folder (like node_modules)
// absolute paths are looked up directly
// the order is respected
descriptionFiles: ["package.json", "bower.json"],
// These JSON files are read in directories
mainFields: ["main", "browser"],
// These fields in the description files are looked up when trying to resolve the package directory
mainFiles: ["index"]
// These files are tried when trying to resolve a directory
aliasFields: ["browser"],
// These fields in the description files offer aliasing in this package
// The content of these fields is an object where requests to a key are mapped to the corresponding value
extensions: [".js", ".json"],
// These extensions are tried when resolving a file
enforceExtension: false,
// If false it will also try to use no extension from above
moduleExtensions: ["-loader"],
// These extensions are tried when resolving a module
enforceModuleExtension: false,
// If false it's also try to use no module extension from above
alias: {
jquery: path.resolve(__dirname, "vendor/jquery-2.0.0.js")
}
// These aliasing is used when trying to resolve a module
}
The chunk loading stuff now relies on Promise
being available. This means you need to provide a Promise
polyfill for older browsers.
The ES6 spec uses promises and I don't want to include a Promise polyfill in every bundle. So it's up the application developer to provide the polyfill if needed.
You need a Object.defineProperty
polyfill for ES6 Module or if using the module
object in other ways than module.exports
, module.id
, module.loaded
or module.hot
.
For ES6 Modules you also need a Function.prototype.bind
polyfill.
That's not new but anyway: You need an Object.keys
polyfill for require.context().keys()
.
The loaders in the configuration now match to the resourcePath
instead of the resource
. This means the query string is no longer included for matching.
This was an issue with bootstrap which complicates the test
for bootstrap fonts and images from /\.svg$/
to /\.svg($|\?)/
. Now you can use the simple form.
The loader in the configuration now resolves relative to the configuration file (or the context
option in the configuration file if specified). This should fix some issues with npm link
ed modules that are outside of the current package.
Another change allows the following syntax to configure loaders:
loaders: [
{
test: /\.css$/,
loaders: [
"style-loader",
{ loader: "css-loader", query: { modules: true } },
{
loader: "sass-loader",
query: {
includePaths: [
path.resolve(__dirname, "some-folder")
]
}
}
]
}
]
The UglifyJsPlugin
no longer puts loaders into minimize mode. The debug
option has been removed. Loaders should no longer read their options from the webpack configuration. Instead you need to provide these options with the LoaderOptionsPlugin
.
new webpack.LoaderOptionsPlugin({
test: /\.css$/, // optionally pass test, include and exclude, default affects all loaders
minimize: true,
debug: false,
options: {
// pass stuff to the loader
}
})
This happens for separation of concern reasons. I want to disallow arbitrary keys in the configuration, to enable configuration validation.
Many plugins now take option objects instead of multiple arguments. This happens because it is easier to extend. They throw an Error when the old argument style is passed.
In webpack 1 the update signal used the Web Messaging API (postMessage
). Webpack 2 uses a standard event emitter to receive the event. This means WebSocket must be inline in the bundle.
webpack-dev-server has inlined mode as default now.
This should allow to use the webpack-dev-server to update code in WebWorkers.
The plugin is no longer needed and occurrence order is on by default.
require.ensure
and AMD require
is now always async, even if the chunk was already loaded.
@rxgx, @rbartoli wants to know if this has changed: