-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Content howto hmr #64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
9853fa2
69fccee
07161de
bee36a8
40a0beb
81b5eac
ab0dce4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,258 @@ | ||
| --- | ||
| title: How to Configure Hot Module Replacement? | ||
| --- | ||
| Hot Module Replacement (HMR) exchanges, adds, or removes modules while an | ||
| application is running without a page reload. | ||
| HMR is particularly useful in applications using a single state tree, | ||
| since components are "dumb" and will reflect the latest application state, even | ||
| after their source is changed and they are replaced. | ||
|
|
||
| Webpack's power lies in its customizablity, and there are MANY ways of configuring HMR | ||
| given the needs of a particular project. The approach described below uses Babel and | ||
| React, but these tools are not necessary for HMR to work. | ||
| If you'd like to see examples of other approaches, | ||
| please request them or, better yet, | ||
| [open up a PR with an addition](https://github.com/webpack/webpack.io)! | ||
|
|
||
| ##Project Config | ||
| This guide will be demonstrating the use of HMR with Babel, | ||
| React, and PostCSS (using CSS Modules). | ||
| To follow along, please add the following deps to your `package.json`: | ||
|
|
||
| To use HMR, you'll need the following dependencies: | ||
|
|
||
| ```shell | ||
| npm install --save-dev [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] | ||
| ``` | ||
|
|
||
| In addition, for the purposes of this walkthrough, you'll need: | ||
|
|
||
| ```shell | ||
| npm install --save [email protected] [email protected] | ||
| ``` | ||
|
|
||
|
|
||
| ###Babel Config | ||
| Your `.babelrc` file should look like the following: | ||
|
|
||
| ```js | ||
| { | ||
| "presets": [ | ||
| ["es2015", {"modules": false}], | ||
| //Webpack understands the native import syntax, and uses it for tree shaking | ||
|
|
||
| "stage-2", | ||
| //Specifies what level of language features to activate. | ||
| //State 2 is "draft", 4 is finished, 0 is strawman. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo |
||
| //See https://tc39.github.io/process-document/ | ||
|
|
||
| "react" | ||
| //Transpile React components to JS | ||
| ], | ||
| "plugins": [ | ||
| "react-hot-loader/babel" | ||
| //Enables React code to work with HMR. | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| ###Webpack config | ||
| While there's many ways of setting up your Webpack config - via API, | ||
| via multiple or single config files, etc - here is the basic information | ||
| you should have available. | ||
|
|
||
| ```js | ||
| const { resolve } = require('path'); | ||
| const webpack = require('webpack'); | ||
|
|
||
| module.exports = env => { | ||
| return { | ||
| entry: [ | ||
| 'react-hot-loader/patch', | ||
| //activate HMR for React | ||
|
|
||
| 'webpack-dev-server/client?http://localhost:8080', | ||
| //bundle the client for webpack dev server | ||
| //and connect to the provided endpoint | ||
|
|
||
| 'webpack/hot/only-dev-server', | ||
| //bundle the client for hot reloading | ||
| //only- means to only hot reload for successful updates | ||
|
|
||
|
|
||
| './index.js' | ||
| //the entry point of our app | ||
| ], | ||
| output: { | ||
| filename: 'bundle.js', | ||
| //the output bundle | ||
|
|
||
| path: resolve(__dirname, 'dist'), | ||
|
|
||
| publicPath: '/' | ||
| //necessary for HMR to know where to load the hot update chunks | ||
| }, | ||
|
|
||
| context: resolve(__dirname, 'src'), | ||
|
|
||
| devtool: 'inline-source-map', | ||
|
|
||
| devServer: { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned above, if you enable inline mode here, you can simplify entries. |
||
| hot: true, | ||
| //activate hot reloading | ||
|
|
||
| contentBase: '/dist' | ||
| //match the output path | ||
|
|
||
| publicPath: '/' | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Document why |
||
| //match the output publicPath | ||
| }, | ||
|
|
||
| module: { | ||
| loaders: [ | ||
| { test: /\.js$/, | ||
| loaders: [ | ||
| 'babel', | ||
| ], | ||
| exclude: /node_modules/ | ||
| }, | ||
| { | ||
| test: /\.css$/, | ||
| loaders: [ | ||
| 'style', | ||
| 'css-loader?modules', | ||
| 'postcss-loader', | ||
| ], | ||
| }, | ||
| ], | ||
| }, | ||
|
|
||
| plugins: [ | ||
| new webpack.HotModuleReplacementPlugin(), | ||
| //activates HMR | ||
|
|
||
| new webpack.NamedModulesPlugin(), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mention why |
||
| //prints more readable module names in the browser console on HMR updates | ||
| ], | ||
| } | ||
| }; | ||
| ``` | ||
|
|
||
| There's a lot going on above, and not all of it is related to HMR. | ||
| You may benefit from reading the | ||
| [full documentation](https://webpack.github.io/docs/webpack-dev-server.html) | ||
| on webpack dev server, and the [other articles](https://webpack.github.io/webpack.io/concepts/) | ||
| here on webpack.io. | ||
|
|
||
| The basic assumption here is that your JS entry is located at `./src/index.js`, | ||
| and that you're using CSS Modules for your styling. | ||
|
|
||
| Please see the comments inline that explain each portion of the config. The main | ||
| areas to look are the `devServer` key and the `entry` key. The `HotModuleReplacementPlugin` is | ||
| also necessary to include in the `plugins` array. | ||
|
|
||
| There are two modules included here for the purposes of this guide. | ||
| The react-hot-loader addition to the entry, as noted above, is necessary to enable | ||
| HMR with React components. The NamedModulesPlugin is a useful addition | ||
| to better understand what modules are being updated when using HMR. | ||
|
|
||
| ###Code | ||
| In this guide, we're using the following files: | ||
|
|
||
| ```js | ||
| // ./src/index.js | ||
| import React from 'react'; | ||
| import ReactDOM from 'react-dom'; | ||
| import { AppContainer } from 'react-hot-loader' | ||
|
|
||
| import App from './components/App'; | ||
|
|
||
| const render = () => { | ||
| ReactDOM.render( | ||
| <AppContainer> | ||
| <App/> | ||
| </AppContainer>, | ||
| document.getElementById('root') | ||
| ); | ||
| }; | ||
|
|
||
| render(); | ||
|
|
||
| // Hot Module Replacement API | ||
| if (module.hot) { | ||
| module.hot.accept('./components/App', render); | ||
| } | ||
|
|
||
|
|
||
| // ./src/components/App.js | ||
| import React from 'react'; | ||
| import styles from './App.css'; | ||
|
|
||
| const App = () => ( | ||
| <div className={styles.app}> | ||
| <h2>Hello, </h2> | ||
| </div> | ||
| ); | ||
|
|
||
| export default App; | ||
| ``` | ||
|
|
||
| ```css | ||
| // ./src/components/App.css | ||
| .app { | ||
| text-size-adjust: none; | ||
| font-family: helvetica, arial, sans-serif; | ||
| line-height: 200%; | ||
| padding: 6px 20px 30px; | ||
| } | ||
| ``` | ||
|
|
||
| Now, the above code is using React, but it doesn't need to be. In fact, | ||
| the only thing that matters above is the code refering to `module`. | ||
| First, we wrap the HMR code inside of `module.hot` check; | ||
| webpack exposes `module` to the code, and if we are running with `hot: true` configured, | ||
| we'll enter the inside of the conditional. | ||
|
|
||
| While the module API offers more options than what's above, the most | ||
| important element is the `module.hot.accept` call. | ||
| It specific how to handle changes to specific dependencies. | ||
|
|
||
| So in this case, `module.hot` will fire the `render` method ONLY | ||
| when `src/components/App.js` changes! Note that would also include when the | ||
| dependencies of `App.js` change - | ||
| so the `render` method will file not just for changes made directly to the | ||
| source of `App.js`, but also changes made to `App.css`, since `App.css` | ||
| is included in `App.js`. | ||
|
|
||
| ###Package.json | ||
| Finally, we need to start up webpack dev server to bundle our code and see HMR in action. | ||
| We can use the following package.json entry: | ||
|
|
||
| ```js | ||
| "start" : "webpack-dev-server --env.dev", | ||
| ``` | ||
|
|
||
| Run `npm start`, open up your browser to `localhost:8080`, | ||
| and you should see the folling entries printed in your console.log: | ||
|
|
||
| ``` | ||
| dev-server.js:49[HMR] Waiting for update signal from WDS... | ||
| only-dev-server.js:74[HMR] Waiting for update signal from WDS... | ||
| client?c7c8:24 [WDS] Hot Module Replacement enabled. | ||
| ``` | ||
|
|
||
| Go ahead and edit and save your App.js file. | ||
| You should see something like the following in your console.log: | ||
|
|
||
| ``` | ||
| [WDS] App updated. Recompiling... | ||
| client?c7c8:91 [WDS] App hot update... | ||
| dev-server.js:45 [HMR] Checking for updates on the server... | ||
| log-apply-result.js:20 [HMR] Updated modules: | ||
| log-apply-result.js:22 [HMR] - ./components/App.js | ||
| dev-server.js:27 [HMR] App is up to date. | ||
| ``` | ||
| Note that HMR specifies the paths of the updated modules. | ||
| That's because we're using the NamedModules plugin! | ||
|
|
||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This bit could use a conclusion as per writer's guide.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could also link to my chapter related to the topic. The React chapter has related info too. |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cc @bebraw
I think since there are many different HMR recipes, maybe we could structure this in like How-To -> HMR Recipies -> Babel/React/Redux
I think that way we encourage other recipes in here for many state libraries and frameworks. And it reinforces a much asked 'cookbook' for certain topics.