A deterministic moduleId keeps the vendor hash still

webpack 4 numbered modules in resolution order, so adding one import renumbered everything after it and changed the vendor chunk hash.

optimization: {
  moduleIds: 'deterministic',   // hashed from the module path
  chunkIds: 'deterministic',
  runtimeChunk: 'single',       // the map lives here, and
                                // it changes every build
}

// before: every deploy invalidated vendor.js for every
//         returning visitor — about 900 KB, needlessly
// after:  vendor.js changes when a dependency changes

Separating the runtime is the other half: it holds the module map, so it changes on every build, and leaving it inside the vendor chunk means the vendor hash still moves. Together they are the difference between a returning visitor downloading nothing and downloading the whole bundle after every deploy. Both are defaults in webpack 5 production mode, which is one of the better reasons to upgrade.