From e7cbebc52537a57de7ba0f2409631a499c090978 Mon Sep 17 00:00:00 2001 From: Sergio Emir Baidon Carrillo Date: Mon, 17 Jul 2017 09:39:26 -0500 Subject: [PATCH 1/5] Rewrite hot-module-replacement.md Initial Commit --- content/guides/hot-module-replacement.md | 1 + 1 file changed, 1 insertion(+) diff --git a/content/guides/hot-module-replacement.md b/content/guides/hot-module-replacement.md index 60f2e82aba0c..3107e16e3be3 100644 --- a/content/guides/hot-module-replacement.md +++ b/content/guides/hot-module-replacement.md @@ -10,6 +10,7 @@ contributors: - joshsantos - drpicox - skipjack + - sbaidon related: - title: Concepts - Hot Module Replacement url: /concepts/hot-module-replacement From 6e03643a796f2317caba924a8a8a33931eb3bdae Mon Sep 17 00:00:00 2001 From: Sergio Emir Baidon Carrillo Date: Mon, 17 Jul 2017 09:42:37 -0500 Subject: [PATCH 2/5] First changes align examples to previous guides --- content/guides/hot-module-replacement.md | 201 +++++++++++++++-------- 1 file changed, 133 insertions(+), 68 deletions(-) diff --git a/content/guides/hot-module-replacement.md b/content/guides/hot-module-replacement.md index 3107e16e3be3..b4f22667b51a 100644 --- a/content/guides/hot-module-replacement.md +++ b/content/guides/hot-module-replacement.md @@ -18,117 +18,182 @@ related: url: /api/hot-module-replacement --- +T> This guide extends on code examples found in the [`Development`](/guides/development) guide. + Hot Module Replacement (or HMR) is one of the most useful features offered by webpack. It allows all kinds of modules to be updated at runtime without the need for a full refresh. This page focuses on __implementation__ while the [concepts page](/concepts/hot-module-replacement) gives more details on how it works and why it's useful. W> __HMR__ is not intended for use in production, meaning it should only be used in development. See the [building for production guide](/guides/production) for more information. - ## Enabling HMR -Enabling this feature is actually fairly simple. Let's take a look at how to set it up with [webpack-dev-server](https://github.com/webpack/webpack-dev-server)... - -``` js -const path = require('path'); -const webpack = require('webpack'); - -module.exports = { - entry: './index.js', +Enabling this feature is actually fairly simple. All we need to do is update our [webpack-dev-server](https://github.com/webpack/webpack-dev-server) configuration, and use webpack's built in HMR plugin. - plugins: [ - new webpack.HotModuleReplacementPlugin() // Enable HMR +``` diff + const path = require('path'); + const HtmlWebpackPlugin = require('html-webpack-plugin'); ++ const webpack = require('webpack'); + + module.exports = { + entry: { + app: './src/index.js', + print: './src/print.js' + }, + devtool: 'inline-source-map', + devServer: { + contentBase: './dist', ++ hot: true + }, + plugins: [ + new HtmlWebpackPlugin({ + title: 'Hot Module Replacement' + }), ++ new webpack.HotModuleReplacementPlugin() ], - output: { - filename: 'main.js', - path: path.resolve(__dirname, 'dist'), - publicPath: '/' - }, - - devServer: { - hot: true, // Tell the dev-server we're using HMR - contentBase: path.resolve(__dirname, 'dist'), - publicPath: '/' + filename: '[name].bundle.js', + path: path.resolve(__dirname, 'dist') } }; ``` +You can also use the CLI to modify the [webpack-dev-server](https://github.com/webpack/webpack-dev-server) configuration with the following command: `webpack-dev-server --hotOnly`. -Not too bad, huh? Let's test it out using `module.hot.accept`... +Now let's update the `index.js` file so that when a change inside `print.js` is detected we tell webpack to accept the updated module. __index.js__ ``` js -import Library from './library'; +import _ from 'lodash'; +import printMe from './print.js'; if (module.hot) { - module.hot.accept('./library', function() { - console.log('Accepting the updated library module!'); - Library.log(); + module.hot.accept('./print.js', function() { + console.log('Accepting the updated printMe module!'); + printMe(); }) } + +function component() { + var element = document.createElement('div'); + var btn = document.createElement('button'); + + element.innerHTML = _.join(['Hello', 'webpack'], ' '); + + btn.innerHTML = 'Click me and check the console!'; + btn.onclick = printMe; + + element.appendChild(btn); + + return element; +} + +document.body.appendChild(component()); ``` +Start changing the `console.log` statement in `print.js`, and you should see the following output in the browser console. -__library.js__ +__print.js__ -``` js -export default { - log() { - // Change this after the server is started to test - console.log('Initial log...') - } +``` diff +export default function printMe() { +- console.log('I get called from print.js!'); ++ console.log('Updating print.js...') } ``` -Start changing the `console.log` statement in `library.js`, to `'Second log...'` for example, and you should see the following output in the browser console... +__console__ ``` diff [HMR] Waiting for update signal from WDS... -main.js:9998 Initial log... -main.js:9468 [WDS] Hot Module Replacement enabled. -+ 2main.js:9468 [WDS] App updated. Recompiling... -+ main.js:9468 [WDS] App hot update... -+ main.js:9912 [HMR] Checking for updates on the server... -+ main.js:9982 Accepting the updated library module! -+ 0.1bafc70….hot-update.js:11 Second log... -+ main.js:9955 [HMR] Updated modules: -+ main.js:9957 [HMR] - ./src/library.js -+ main.js:9894 [HMR] App is up to date. +main.js:4395 [WDS] Hot Module Replacement enabled. ++ 2main.js:4395 [WDS] App updated. Recompiling... ++ main.js:4395 [WDS] App hot update... ++ main.js:4330 [HMR] Checking for updates on the server... ++ main.js:10024 Accepting the updated printMe module! ++ 0.4b8ee77….hot-update.js:10 Updating print.js... ++ main.js:4330 [HMR] Updated modules: ++ main.js:4330 [HMR] - 20 ++ main.js:4330 [HMR] Consider using the NamedModulesPlugin for module names. ``` +## Gotchas +Hot Module Replacement can be tricky. To show this, let's go back to our working example. If you go ahead and click the button on the example page, you will realize the console is printing the old `printMe` function. -## Gotchas +This is happening because the button's `onclick` event handler is still bind to the original `printMe` function. -Hot Module Replacement can be tricky. For example, let's say I have the following class: +To make this work with HMR we need to update that binding to the new `printMe` function using `module.hot.accept` ``` js -class Logger { - log(text) { - console.log('Logging some text: ', text) - } +import _ from 'lodash'; +import printMe from './print.js'; + +if (module.hot) { + module.hot.accept('./print.js', function() { + console.log('Accepting the updated printMe module!'); + document.body.removeChild(element); + element = component(); // update binding now that new printMe module has been acepted + document.body.appendChild(element); + }) } -``` -Even if the underlying module containing this class is patched with new code, any existing instances of the class still have the old `log` method. Meaning if we changed what that method does, it wouldn't be reflected in those old instances unless we re-instantiate them somehow using `module.hot.accept`. +let element = component(); -This is just one example, but there are many others that can easily trip people up. Luckily, there are a lot of loaders out there, some mentioned below, that will make using this process much easier. +function component() { + var element = document.createElement('div'); + var btn = document.createElement('button'); + element.innerHTML = _.join(['Hello', 'webpack'], ' '); -## HMR with Stylesheets + btn.innerHTML = 'Click me and check the console!'; + btn.onclick = printMe; // onclick event is bind to the original printMe function -Hot Module Replacement with CSS is actually fairly straightforward with the help of the `style-loader`. This loader uses `module.hot.accept` behind the scenes to patch `