0
votes

I have a React + Webpack + Express setup. The React files are bundled by Webpack and stored in a /dist directory. In development mode Webpack is running in the background in watch mode. New changes are updated inside the dist directory. The Express server is running separately from the Webpack process. On request localhost:3000/ the Express server sends the client the dist/index.html file.

I want to enable a page reload everytime the Webpack watcher is done compiling the React files (Frontend). What I have seen so far is people hot reloading the express server via webpack, the moment something changes, but that doesn’t update the client.

Is there a way to hot-reload the client (via Express?) when Webpack re-bundles the frontend code?

webpack.config.js

const path = require('path');
const Dotenv = require('dotenv-webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
    mode: 'development',
    devtool: 'source-map',
    entry: {
        main: './src/index.js',
        vendor: ['semantic-ui-react'],
    },
    output:{
        path: path.join(__dirname, '/dist'),
        filename: '[name].js'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader'
                }
            },
            {
                test: /\.scss$/i,
                use: [{
                    loader: "style-loader"
                }, {
                    loader: "css-loader", options: {
                        sourceMap: true
                    }
                }, {
                    loader: "sass-loader", options: {
                        sourceMap: true
                    }
                }]
            },
            {
                test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/,
                use: {
                    loader: 'url-loader',
                    options: {
                        limit: 100000,
                    },
                },
            },
            {
                test: /\.css$/,
                include: /node_modules/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                        options: {
                            // you can specify a publicPath here
                            // by default it uses publicPath in webpackOptions.output
                            publicPath: '../',
                            hmr: process.env.NODE_ENV === 'development',
                        },
                    },
                    'css-loader',
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './src/index.html'
        }),
        new Dotenv(),
        new MiniCssExtractPlugin('styles.css')
    ]
};

server.js

const express = require('express');
const axios = require('axios');
const app = express();
const port = 3000;

app.set('view engine', 'ejs');
app.use(express.static('dist'));

app.get('/', (req, res) => {
    res.sendFile(__dirname + '/dist/index.html');
});

app.listen(port, () => console.log('App listening on port 3000!'));
1

1 Answers

0
votes

Developing this way is sub-optimal in several ways:

1.) The way you're currently developing is considered for production only (which is kind of an old-school (2015) method that has been mostly deprecated since compilation times can be very time consuming as the application grows and requires manual process restarts). While you could use nodemon, which restarts the process when a change has been saved, it breaks HMR. Therefore, a better approach would be to use webpack-dev-server to create a development-only server that doesn't require express to serve compiled assets. Instead, this will create quicker incremental updates on saves, which are then re-served after a HMR event. When the app is ready for production, only then will you build the entire application (using webpack) and then have express serve these production assets.

2.) Your entire application code appears to be bundled into one large JS + CSS file. Meaning, if your app has multiple routes, then no matter which route the user lands on, the entire application must be downloaded. This is very detrimental in overall performance and unnecessary wasteful in bandwidth. Ideally, you'd want a PWA (progressive web app) that splits code into chunks and will only be downloaded when needed/requested. The exception would be if you're building a UI library using Webpack, but that doesn't appear to be the situation.

3.) Using the devtool: 'source-map' is very resource heavy, creates longer compilation times, and has been mostly replaced by 'cheap-module-source-map' (for quicker recompilations). It's not a perfect replacement, but it's pretty close.

Instead of including a mile-long Webpack configuration with notes, you can take a look at my react-starter-kit (specifically the config directory and webpack.config.js file) which already includes the dev-server setup with notes. By no means do you need to use this boilerplate, but it should give an idea of how you can refactor your Webpack config to be more flexible for development.