preface

Why can’t our project be hot updated all of a sudden? So I tried it and, gee, it didn’t work. Then I tried several versions and found that the hot update had been unavailable for a long time. It is no longer possible to roll back to a very old version (webPack did a big configuration optimization earlier), so you can only find the root cause of the problem in this new version. The good news is that all code can be debugged, which is one of the biggest reasons I like JS. So I rolled up my sleeves and started asking questions.

Find the Root of the problem

From the console print:

[HMR] Nothing hot updated.

Webpack HRM unexpectedly judged that I did not have the content of hot update, obviously I changed one of the modules.

So we started tracing back to the process-update.js file to find this call:

var result = module.hot.check(false, cb);
Copy the code

It turns out that hot updates are checked, of course, because of the __webpack_hMR request, which is a websocket.

We then follow up and find that the check performs a request:

http://127.0.0.1:9093/dist/66262610103662a21e81.hot-update.json

Then I checked that the request was ok and brought back the changed trunk and hash value:

We know which chunk has been updated, so we continue to trace the code. When the JSON file request comes back, we call the callback function, and suddenly the breakpoint hits the file: boostrap XXXXXXXX

A very strange document!

But here’s where the breakpoint is actually executed, and something weird happens:

What you didn’t notice was that the code said:

var chunkId = 1;
Copy the code

But Chrome changed it to chunkId = 4, which is a bug in Chrome.

But think carefully, impossible ah, others clearly can use, affirmation is I have what place negligence!

Because the chunkId value is not the same as the chunkId returned from hot-update.json, the code will not request another file: hot-update.js, which will not re-render the component.

Seems to have found the root of the problem, but why is chunkId an incorrect value?

Scratching my head. In desperation, I took the initial version of the project and ran it to see what the difference was.

This is a tried-and-true method that has been used to solve many strange problems.

A comparison of older versions

After the old version runs, it is no problem to modify the information casually. So I looked through the WebPack configuration and didn’t see anything special. However, after compiling, we found a file difference: the size of the vendor file of the new version is different from that of the old version!

Old version:

vendor-623881.js   726 kB       9  [emitted]  [big]  vendor
Copy the code

The new version:

vendor-a80884.js   721 kB       9  [emitted]  [big]  vendor
Copy the code

I quickly opened the file comparator and noticed that there was a real difference between the two:

At the beginning of the old version there was this code:

/******/ // install a JSONP callback for chunk loading /******/ var parentJsonpFunction = window["webpackJsonp"]; /******/ window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) { /******/ // add "moreModules" to the modules object, /******/ // then flag all "chunkIds" as loaded and fire callback /******/ var moduleId, chunkId, i = 0, resolves = [], result; /******/ for(; i < chunkIds.length; i++) { /******/ chunkId = chunkIds[i]; /******/ if(installedChunks[chunkId]) { /******/ resolves.push(installedChunks[chunkId][0]); /******/ } /******/ installedChunks[chunkId] = 0; /******/ } /******/ for(moduleId in moreModules) { /******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) { /******/ modules[moduleId] = moreModules[moduleId]; /******/ } /******/ } /******/ if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules); /******/ while(resolves.length) { /******/ resolves.shift()(); /******/ } /******/ if(executeModules) { /******/ for(i=0; i < executeModules.length; i++) { /******/ result = __webpack_require__(__webpack_require__.s = executeModules[i]); /******/ } /******/ } /******/ return result; / * * * * * * /};Copy the code

Then, in the old version, the weird code looked something like this:

for(var chunkId in installedChunks)
{ // eslint-disable-line no-lone-blocks
  /*globals chunkId */
  hotEnsureUpdateChunk(chunkId);
}
Copy the code

It just seems logical.

During the comparison, I also found a point that made me realize the root of the problem:

The code for vendor.js in the new version where the problem is is like this:

/******/ var chunkId = 4; /******/ { // eslint-disable-line no-lone-blocks /******/ /*globals chunkId */ /******/ hotEnsureUpdateChunk(chunkId); / * * * * * *}Copy the code

Isn’t this assignment the same value that the Chrome debugger printed when the error occurred? So I opened all the compiled chunk files and found that each chunk file had almost the same piece of code about hot-Module. The only difference was that the chunkId value was assigned differently, equal to its own chunkId value.

And then I look at the duplicate code and I see that the code execution overwrites each other! This results in a chunkId file updating the hot-Module code that executes incorrectly. So no updates detected!

The solution

Now that we know why, it’s easy to think of a plugin for WebPack from the root of the problem: the CommonsChunkPlugin. The old version has this plugin configured, but the new version does not have this plugin in the optimization process. Moreover, the ‘react-hot-loader/patch’ portal is missing in the vendor entry file, which allows hot updates to be implemented in react.

Attached with simplified WebPack hot update important configuration:

entry: {
  'test': [
    './src/client/test/index.js',
  ],
  'vendor': [
    'react-hot-loader/patch',
    'webpack-hot-middleware/client?path=http://' + host + ':' + port + "/__webpack_hmr"
  ]
},
plugins: [
  new webpack.HotModuleReplacementPlugin(),
  ...
  new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor',
    minChunks: Infinity,
  }),
]
Copy the code

The react-hot-loader configuration and usage are not posted, you can refer to the official documentation.

conclusion

In this case, I think it is sometimes necessary to look at the code of the toolkit you are referencing. You can learn more about debugging than you know, for example, the WebPack hot update process, and the commonChunkPlugin. So in the future, when you encounter a problem, don’t just think of Google, try to solve the problem yourself might be a better solution.