webpack 4: Changes Part 1 (week 24–25)

2017/09/11–2017/09/24

In the last weeks I finished and merged all queued PRs with breaking changes and features for webpack 4. Here is a summary of the important ones, so you are not surprised when you read it in the changelog:

Sets

Many classes now use Sets (or SortableSets) on the API level now. This change may break plugins which try to modify these arrays.

Reason

Performance. “Deleting” and “Adding once” is now O(1) instead of O(n).

StackedSetMap

The Parser also uses Arrays/Objects to track renaming and variable definitions. The additional challenge here was the inheriting in AST scopes. Previously we just added definitions to the Array. When entering a scope we stored the array length, so we could reset length back to this value when leaving the scope. This was pretty efficient because arrays don’t have to be copied. For Objects we created a prototype chain

The same was not possible when using Sets/Maps and copying decreased the performance. So I created a new class StackedSetMap which combines the benefits for prototype chains with Maps. It’s implemented with a stack of Maps. When entering a scope the stack of maps from the current is copied (shallow) and a new Map is added on top of these. Adding items always adds them to the topmost Map. Reading items walks through all maps of the stack to get the value, and caches the values in the topmost map. Deleting a value adds a thumbstone object as value (thanks to LevelDB, I recommend reading the datastructure). The Set-version can be implemented on top of the Map version.

Chunk loading with Array push

In webpack 3 and lower chunks are loaded in this way:

The first bundle file (entry chunk) contains the webpack runtime and registered a global function by default named webpackJsonp. All other chunks call this function on load. So this is similar to JSONP, but not the same because the content isn’t JSON but true JS (mainly functions).

The issue with this approach is that runtime has to be loaded before any other chunk. → No async scripts, runtime chunk contains chunk manifest.

Now webpack 4 uses a new way:

(window.webpackJsonp = window.webpackJsonp || []).push([...])

This doesn’t require a registered global function from the runtime. It pushes the arguments ... into a array (created if not existent yet). The runtime overrides the push function of the array with the chunk loading function and processed all “queued” items from the array.

  • This allow chunks to be executed in any order (<script async>) which is really great if some files are already cached.
  • This allow to put the webpack runtime into any chunk. In many cases no need for a manifest chunk anymore, you can put the runtime into the app chunk and keep the vendor chunk cached.

Sideeffect-free modules

I already talked about this in a previous blog post (“Pure Modules”). The flag was renamed to "side-effects": false. Take a look at the example here.

Deprecated stuff

The following API-things were removed:

  • module.loaders (→ module.rules)
  • Compilation.notCacheable
  • HMR orignalErrororiginalError
  • loaderContext resolveSync ( → resolve)
  • SourceMapDevToolPlugin with only a string argument
  • loaderContext options ( → LoaderOptionsPlugin for incompatible loaders, but eventually loaders should be fixed)


webpack is not backed by a big company, unlike many other big Open Source products. The development is funded by donations. Please consider donating if you depend on webpack… (Ask your boss!)

Special thanks to these sponsors: (Top 5)

  • Segment (Data Infrastructure) donated a total of $24,000
  • Trivago (Hotel metasearch) donated a total of $20,000
  • ag-Grid (DataGrid) donated a total of $17,500
  • Capital One (Bank) donated a total of $12,000
  • Slack (Web-IM) donated a total of $8,000
  • Full list