抹桥的博客
Language
Home
Archive
About
GitHub
Language
主题色
250
1832 words
9 minutes
Webpack 1 to 2 Upgrade Guide
2017-01-20

resolve.root,resolve.fallback,resolve.modulesDirectories#

These configuration items have all been replaced by a single configuration: resolve.modules. See resolving for more details.

resolve: {
-   root: path.join(__dirname, "src")
+   modules: [
+     path.join(__dirname, "src"),
+     "node_modules"
+   ]
}
 

resolve.extensions#

This option no longer requires an empty string. Its behavior has been moved to resolve.enforceExtension. See resolving for more details.

resolve.*#

Many changes have been made here. Since they are not widely used, they will not be listed individually. See resolving for more details.

module.loaders is now module.rules#

The old loader configuration has been replaced by a more powerful rules system, which allows configuring loaders and more. For consistency, the old module.loaders syntax is still valid and usable. The new naming convention is easier to understand, making it a good reason to upgrade your configuration using module.rules.

  module: {
-   loaders: [
+   rules: [
      {
        test: /\.css$/,
-       loaders: [
+       use: [
          {
            loader: "style-loader"
          },
          {
            loader: "css-loader",
-           query: {
+           options: {
              modules: true
            }
        ]
      },
      {
        test: /\.jsx$/,
        loader: "babel-loader", // Do not use "use" here
        options: {
          // ...
        }
      }
    ]
  }

Chaining loaders#

Just like in webpack 1, loaders can be chained, passing the result of one loader to the next. Using rule.use, use can be set to a list of loaders. In webpack 1, loaders were chained together using !. This style is now only supported within module.loaders.

  module: {
-   loaders: {
+   rules: {
      test: /\.less$/,
-     loader: "style-loader!css-loader!less-loader"
+     use: [
+       "style-loader",
+       "css-loader",
+       "less-loader"
+     ]
    }
  }

The feature of automatically adding the -loader suffix has been removed#

The -loader suffix can no longer be omitted when importing loaders.

module: {
  rules: [
    {
      use: [
        -"style",
        +"style-loader",
        -"css",
        +"css-loader",
        -"less",
        +"less-loader",
      ],
    },
  ];
}

However, you can still achieve the previous behavior using the resolveLoader.moduleExtensions configuration, but we do not recommend it.

+ resolveLoader: {
+   moduleExtensions: ["-loader"]
+ }

json-loader is no longer needed#

When no corresponding loader is configured for JSON files, webpack will automatically use json-loader to load them.

  module: {
    rules: [
-     {
-       test: /\.json/,
-       loader: "json-loader"
-     }
    ]
  }

Webpack chose to do this to smooth out the differences between webpack, Node.js, and Browserify environments.

Loaders in configuration resolve relative to context#

In webpack 1, configured loaders resolved relative to the matched file. However, in webpack 2, configured loaders resolve based on the context setting.

This resolves issues of duplicate module imports caused by using npm link or referencing modules outside the context.

You might have solved this problem with a hack like this:

  module: {
    rules: [
      {
        // ...
-       loader: require.resolve("my-loader")
+       loader: "my-loader"
      }
    ]
  },
  resolveLoader: {
-   root: path.resolve(__dirname, "node_modules")
  }

module.preLoaders and module.postLoaders have been removed.

  module: {
-   preLoaders: [
+   rules: [
      {
        test: /\.js$/,
+       enforce: "pre",
        loader: "eslint-loader"
      }
    ]
  }

UglifyJsPlugin sourceMap#

The default sourceMap option in UglifyJsPlugin has changed from true to false. This means if you need source map functionality in your minified code, you must manually configure sourceMap: true.

  devtool: "source-map",
  plugins: [
    new UglifyJsPlugin({
+     sourceMap: true
    })
  ]

UglifyJsPlugin warnings#

Similar to the previous point, the default compress.warnings option in UglifyJsPlugin has changed from true to false. If you need to see UglifyJS warnings, you must set compress.warnings to true.

  devtool: "source-map",
  plugins: [
    new UglifyJsPlugin({
+     compress: {
+       warnings: true
+     }
    })
  ]

UglifyJsPlugin minimize loaders#

UglifyJsPlugin no longer switches loaders in minimize mode. minimize: true must now always be set in the configuration passed to the loader. See the documentation for more details.

In webpack 3 or later versions, the loader minimization mode feature will be removed.

BannerPlugin - breaking change#

BannerPlugin no longer supports passing two arguments; instead, it expects a single object.

  plugins: [
-    new webpack.BannerPlugin('Banner', {raw: true, entryOnly: true});
+    new webpack.BannerPlugin({banner: 'Banner', raw: true, entryOnly: true});
  ]

OccurrenceOrderPlugin is now a default setting#

No longer needs to be configured manually.

  plugins: [
-   new webpack.optimize.OccurrenceOrderPlugin()
  ]

ExtractTextWebpackPlugin - breaking change#

ExtractTextWebpackPlugin 1.0.0 does not work correctly in webpack 2; you need to install ExtractTextPlugin V2.

npm install --save-dev extract-text-webpack-plugin@beta

The configuration differences are mainly syntactic.

ExtractTextPlugin.extract

module: {
  rules: [
    test: /.css$/,
-    loader: ExtractTextPlugin.extract("style-loader", "css-loader", { publicPath: "/dist" })
+    loader: ExtractTextPlugin.extract({
+      fallbackLoader: "style-loader",
+      loader: "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
+  })
]

Full dynamic requires now fail by default#

A dependency determined by an expression will now create an empty context instead of the previous context that included the entire folder.

It’s best to refactor this code, as it won’t work with ES2015 modules. If that’s not possible for you, you can use ContextReplacementPlugin to specify the correct path for the compiler.

Using custom parameters in CLI and configuration files#

If you’ve been misusing CLI arguments to pass custom parameters into your configuration, like this:

webapck --custom-stuff
// webpack.config.js
var customStuff = process.argv.indexOf("--custom-stuff") >= 0;
/* ... */
module.exports = config;

You’ll find this is no longer allowed, as the CLI is now stricter than before.

Instead, there is now an interface for passing arguments to the configuration. Future tools will be based on this.

webpack --env.customStuff
module.exports = function (env) {
  var customStuff = env.customStuff;
  /* ... */
  return config;
};

See CLI

require.ensure and AMD require are now asynchronous#

These functions have become asynchronous, replacing the previous behavior where their callbacks would execute synchronously if the code block had already been loaded.

require.ensure now relies on native Promise. If you use require.ensure in an environment that does not support Promise, you will need a polyfill.

Loader configuration must use options#

You can no longer configure loaders via a custom property in webpack.config.js. It must be done through options. The ts property configuration below is no longer valid in webpack 2.

module.exports = {
  ...
    module: {
  rules: [{
    test: /\.tsx?$/,
    loader: 'ts-loader'
  }]
},
// does not work with webpack 2
ts: { transpileOnly: false }
}

What are options?#

Good question. Strictly speaking, it can be two things; both are ways to configure a loader. A typical options is called query, which is a string that can be appended after the loader name. It’s more like a query string, but actually has greater powers.

module.exports = {
  ...
  module: {
  rules: [{
    test: /\.tsx?$/,
    loader: 'ts-loader?' + JSON.stringify({ transpileOnly: false })
   }]
  }
}
 

It can also be a specified object, provided alongside the loader:

module.exports = {
  ...
    module: {
  rules: [{
    test: /\.tsx?$/,
    loader: 'ts-loader'
    options:  { transpileOnly: false }
  }]
}
}
 

LoaderOptionsPlugin context#

Some loaders need to read context information from the configuration file. This needs to be set in the loader’s options for the long term.

For compatibility with older loaders, you can pass it to the loader via this plugin:

plugins: [
  +   new webpack.LoaderOptionsPlugin({
    +     options: {
  +       context: __dirname
  +     }
+   })
]
 

debug#

In webpack 1, the debug option was used to switch loaders into debug mode.

In webpack 3 or later versions, this mode will be removed.

For compatibility with older loaders, you can pass arguments to the loader via this plugin:

- debug: true,
  plugins: [
+   new webpack.LoaderOptionsPlugin({
+     debug: true
+   })
]

ES2015 Code Splitting#

In webpack 1, you could lazy-load code chunks using require.ensure:

require.ensure([], function (require) {
  var foo = require("./module");
});

In ES2015, we use import() as a method to dynamically load ES2015 modules at runtime.

Webpack treats import() as a split point and separates the loaded code into a distinct chunk.

import() takes a module name as an argument and returns a Promise object.

function onClick() {
  import("./module")
    .then((module) => {
      return module.default;
    })
    .catch((err) => {
      console.log("Chunk loading failed");
    });
}

Good news: chunk loading failures can now be handled because they are Promise-based.

Caveat: require.ensure allows conveniently specifying a chunk name via its third argument, but the import API does not yet support this feature. If you still rely on this functionality, you can continue to use require.ensure.

require.ensure(
  [],
  function (require) {
    var foo = require("./module");
  },
  "custom-chunk-name",
);

If you want to use import with Babel, you need to install the dynamic-import syntax plugin, which is still in Stage 3, to bypass parsing errors. Once this proposal is added to the standard, this will no longer be necessary.

Dynamic expressions#

It’s very likely you’ll need to pass an expression to import(). The handling pattern here is very similar to CommonJS.

import() creates a separate chunk for each possible module.

function route(path, query) {
  return import(`./routes/${path}/route`).then(
    (route) => new route.Route(query),
  );
}
// This creates a separate chunk for each possible route

Mixing ES2015, AMD, and CommonJS#

If it’s AMD and CommonJS, they can be freely mixed. In this case, Webpack’s behavior is similar to Babel or node-eps.

// CommonJS consuming ES2015 Module
var book = require("./book");

book.currentPage;
book.readPage();
book.default === "This is a book";
// ES2015 Module consuming CommonJS
// module.exports map to default
import fs, { readFileSync } from "fs";

// named exports are read from returned object+

typeof fs.readFileSync === "function";
typeof readFileSync === "function";

It’s important to note that you need to tell Babel not to parse these module symbols so that webpack can use them. You can configure this in .babelrc as follows:

babelrc

{
  "presets": [
    ["es2015", { "modules": false }]
  ]
}

Tips#

No changes are required, but these features are very convenient.

String Templates#

Webpack now supports using string templates in expressions. This means you can use them in webpack constructs:

- require("./templates/" + name);
+ require(`./templates/${name}`);

Configuration Promise#

Webpack now supports returning a Promise from the configuration file. This means you can perform asynchronous operations within your configuration.

webpack.config.js

module.exports = function () {
  return fetchLangs().then((lang) => ({
    entry: "...",
    // ...
    plugins: [new DefinePlugin({ LANGUAGE: lang })],
  }));
};

Advanced Loader Matching#

Webpack now supports more ways for loaders to match files.

module: {
  rules: [
    {
      resource: /filename/, // matches "/path/filename.js"
      resourceQuery: /querystring/, // matches "/filename.js?querystring"
      issuer: /filename/, // matches "/path/something.js" if requested from "/path/filename.js"
    },
  ];
}

More Command-Line Options#

Some new command-line options have been added: --define process.env.NODE_ENV="production", see DefinePlugin. --display-depth shows the distance of each module from the entry point --display-used-exports shows which exports of a module are used --display-max-modules sets the maximum number of modules displayed in the output -p also sets process.env.NODE_ENV to “production”

Loader Changes#

Relevant only to loader authors.

Cacheable#

Loaders are now cacheable by default. Loaders must opt-out by returning if they are not cacheable.

  // Cacheable loader
  module.exports = function(source) {
-   this.cacheable();
    return source;
  }
  // Not cacheable loader
  module.exports = function(source) {
+   this.cacheable(false);
    return source;
  }

Complex Options#

Webpack 1 only supported loaders with options that could be JSON.stringify-ed. Webpack 2 supports all plugins receiving a JavaScript object as an argument.

Using complex options introduces a limitation. The options object needs an ident so it can be referenced by other loaders.

Having an ident on the options object means it can be referenced by other inline loaders.

require('some-loader??by-iden!resource')
{
  test: /.../,
  loader: "...",
  options: {
    ident: "by-ident",
    magic: () => return Math.random()
  }
}

This inline syntax should not be used frequently, but it can be used by loader-generated code. Example: style-loader generates a module that requires the remaining request (e.g., to expose CSS).

// style-loader generated code (simplified)
var addStyle = require("./add-style");
var css = require("-!css-loader?{"modules":true}!postcss-loader??postcss-ident");

addStyle(css);

So if you’re using complex options, inform your users about ident.

This article was published on January 20, 2017 and last updated on January 20, 2017, 3181 days ago. The content may be outdated.

Webpack 1 to 2 Upgrade Guide
https://blog.kisnows.com/en-US/2017/01/20/webpack2-migrating-v1-v2/
Author
Kisnows
Published at
2017-01-20
License
CC BY-NC-ND 4.0