Hot Reloading React with webpack and Express

Specs

Our application is currently written using React v0.14.7, webpack v1.12.14 and Express v4.13.4 (all installed via npm). We write all of our code using ES2015 modules.

Before starting down this path, we used Gulp to run webpack on a watch, which worked well… but was cumbersome as we saved files, checked our terminal to be sure that webpack compiled successfully, then switched to the browser to manually refresh the app. Rinse and repeat about 100,000 times per day.

For reference, our initial webpack config looked roughly like:

{
    cache: true,
    entry: {
        pageOne: './src/pageOne.js',
        pageTwo: './src/pageTwo.js',
        pageThree: './src/pageThree.js',
    },
    output: {
        filename: '[name]/bundle.js',
        path: path.join(__dirname, "../public/js/")
    },

    plugins: [
        new SplitByPathPlugin([{
            name: 'vendor',
            path: path.join(__dirname, '..', 'node_modules')
        }])
    ],

    resolve: {
        extensions: ['', '.js', 'map']
    },

    module: {
        loaders: [{
            test: /.jsx?$/,
            exclude: /(node_modules|bower_components)/,
            loader: 'babel',
            query: {
                presets: ['react', 'es2015']
            }
        }]
    }
}

No magic in here — the only really interesting part is the fact we have multiple entry points for webpack, as well as a “vendor” file. Therefore we would expect the following files to be physically created on the filesystem:

/public/js/pageOne/bundle.js
/public/js/pageTwo/bundle.js
/public/js/pageThree/bundle.js
/public/js/vendor/bundle.js

Note: “public” is the web root for my setup; anything in the app accesses the files via “/js/” and not “/public/js/”.

Hot Reloading React

The article I linked earlier “Setting Up Webpack Dev Middleware in Express” did an awesome job explaining the basics of getting webpack hot reloading in place. But there were two problems for me:

  1. The article explains the base scenario of a SINGLE entry point for webpack; configuring multiple entry points isn’t terribly complicated, but it’s complicated enough to throw a wrench in this process
  2. There’s no mention of React in the article

So I’ll skip over the introduction provided in that article and just jump to the meat you’ll need if your situation is like mine.

npm install

First, we need to install some dependencies:

npm install --save-dev webpack-dev-middleware webpack-hot-middleware react-hot-loader

That will install the following versions as of this writing:

  • webpack-dev-middleware v1.6.1
  • webpack-hot-middleware v2.10.0
  • react-hot-loader v1.3.0

webpack config

Next, we’ll need to update our webpack config file. In my case, I chose to create a separate “dev” config file because we will still compile physical files for our production builds. (NOTE: not part of this tutorial)

Create a file named “webpack.dev.config.js” and copy/paste your original “webpack.config” into it. We’re going to modify it slightly — note the comments below:

{
    cache: true,

    // each value on this object is now an array and MUST have the extra modules
    entry: {
        pageOne: ['./src/pageOne.js', 'webpack-hot-middleware/client', 'webpack/hot/dev-server'],
        pageTwo: ['./src/pageTwo.js', 'webpack-hot-middleware/client', 'webpack/hot/dev-server'],
        pageThree: ['./src/pageThree.js', 'webpack-hot-middleware/client', 'webpack/hot/dev-server'],
    },

    // we use ES2015; we will want source maps for development
    devtool: 'source-map',

    // this is a default value; just be aware of it
    target: 'web',

    // "path" is now "/" because we're building our app into memory now rather than a build folder
    // "publicPath" is where the hosted app expects the resources
    output: {
        filename: '[name]/bundle.js',
        path: '/',
        publicPath: 'http://localhost:3000/js/'
    },

    plugins: [
        new SplitByPathPlugin([{
            name: 'vendor',
            path: path.join(__dirname, '..', 'node_modules')
        }]),

        // added this thing!
        new webpack.HotModuleReplacementPlugin()
    ],

    resolve: {
        extensions: ['', '.js', 'map']
    },

    module: {
        loaders: [{
            test: /.jsx?$/,
            exclude: /(node_modules|bower_components)/,

            // "loader" property changed to "loaders" and is now an array!
            loaders: [
                // ORDER MATTERS; "react-hot" needs to be on the left, because webpack processes the loaders from right-to-left
                'react-hot', 

                // webpack forbids the "loader.query" property when you have multiple loaders; use a queryString to pass those details
                'babel?presets[]=react,presets[]=es2015'
            ]
        }]
    }
}

A number of those commented items were stumbling blocks for me; again, here’s hoping this helps someone else.

Express

Next, we need to configure our Express app to optionally use the hot reloading bits:

var express = require('express'),
        app = express(),

        // I extracted some logic to another file; more on that in a moment
        webpackDevHelper = require('./index.dev.js');

    // ...presumably lots of other stuff...

    // we only want hot reloading in development
    if (process.env.NODE_ENV !== 'production') {
        console.log('DEVOLOPMENT ENVIRONMENT: Turning on WebPack Middleware...');
        webpackDevHelper.useWebpackMiddleware(app);
    } else {
        console.log('PRODUCTION ENVIRONMENT');

        //Production needs physical files! (built via separate process)
        app.use('/js', express.static(__dirname + '/public/js'));
    }

    // ...presumably lots of other stuff...

    // Start your express server as usual
    app.start(3000);

That conditional block is really the only thing we’ve added to our usual Express setup. Clearly we’re checking against a NODE_ENV flag — so in production, you’ll want to make sure that is set!

The contents of “index.dev.js” are pretty simple:

var webpack = require('webpack'),
    webpackDevMiddleware = require('webpack-dev-middleware'),
    webpackHotMiddleware = require('webpack-hot-middleware'),
    webpackconfig = require('./webpack.dev.config.js'),
    webpackcompiler = webpack(webpackconfig);

//enable webpack middleware for hot-reloads in development
function useWebpackMiddleware(app) {
    app.use(webpackDevMiddleware(webpackcompiler, {
        publicPath: webpackconfig.output.publicPath,
        stats: {
            colors: true,
            chunks: false, // this reduces the amount of stuff I see in my terminal; configure to your needs
            'errors-only': true
        }
    }));
    app.use(webpackHotMiddleware(webpackcompiler, {
        log: console.log
    }));

    return app;
}

module.exports = {
    useWebpackMiddleware: useWebpackMiddleware
};

HTML

This should go without saying, but clearly you need to include the files in your HTML page.

    

Happy Coding!

At this point, you should be able to fire up your app (having NODE_ENV set to anything other than “production”) and hot reloading React just works.

You’ll see this in your browser console:

You can then begin editing your files. Hot reloading React components will then update auto-magically (assuming nothing requires a full page reload). State is even maintained!

However, I do notice that non-React files (e.g. Flux stores, utility classes) can often require full page reloads. I haven’t fully figured-out if hot reloading those things is possible, but at least I get some console messages from HMR telling me about it.

Production Code?

I mentioned earlier that we use Grunt to create our production builds using webpack. That process involves a few other things (shifting files around the filesystem, etc) — but the important thing is that our production output matches the expected URLs of the hot reloader.

/public/js/

That way I never have to update the HTML script tags.

Resources

I need to give credit to all of the places I found information on the subject:

稿源:aKa Web Design (源链) | 关于 | 阅读提示

本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » 创业投资 » Hot Reloading React with webpack and Express

喜欢 (0)or分享给?

专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录