首页 文章

使用React-hot-loader 3,React-router 4和Webpack-hot-middleware实现Hot Reload

提问于
浏览
1

我试图让React-hot-loader 3与React-hot-loader 3,React-router 4和Webpack-hot-middleware(最新版本,2.18.2)一起工作 .

这是我的 server.js

const express = require('express');
const bodyParser = require('body-parser');
const cookiesMiddleware = require('universal-cookie-express');
/* eslint-disable import/no-extraneous-dependencies */
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const webpackHotServerMiddleware = require('webpack-hot-server-middleware');
/* eslint-enable import/no-extraneous-dependencies */
const clientConfig = require('./webpack.config.dev.client');
const serverConfig = require('./webpack.config.dev.server');

const PORT_NUMBER = process.env.PORT || 3000;
const app = express();

app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookiesMiddleware());
app.use(express.static('public'));

const multiCompiler = webpack([clientConfig, serverConfig]);
const clientCompiler = multiCompiler.compilers[0];

app.use(webpackDevMiddleware(multiCompiler, {
  publicPath: clientConfig.output.publicPath,
  noInfo: true,
  stats: { children: false },
}));
app.use(webpackHotMiddleware(clientCompiler));
app.use(webpackHotServerMiddleware(multiCompiler, {
  serverRendererOptions: { outputPath: clientConfig.output.path },
}));

app.listen(PORT_NUMBER, () => {
  // eslint-disable-next-line no-console
  console.log(`Server listening at port ${PORT_NUMBER}`);
});

我的 client entry point

import React from 'react';
import { render } from 'react-dom';
import { AppContainer } from 'react-hot-loader';

import * as Bundles from './components/Bundles';
import App from './App';

const doRender = () => {
  render(
    <AppContainer>
      <App type="client" />
    </AppContainer>,
    document.getElementById('content'),
  );
};

const splitPoints = window.splitPoints || [];
Promise.all(splitPoints.map(chunk => Bundles[chunk].loadComponent()))
  .then(doRender);

if (module.hot) {
  module.hot.accept('./App', doRender);
}

.babelrc

{
  "plugins": [
    "transform-decorators-legacy",
    "transform-object-rest-spread"
  ],
  "presets": [
    ["es2015", { "modules": false }],
    "react",
    "stage-0"
  ],
  "env": {
    "development": {
      "plugins": ["react-hot-loader/babel"]
    },
    "test": {
      "plugins": ["transform-es2015-modules-commonjs"]
    }
  }
}

看起来我跟着react-hot-loader's README的每一步,但每次我更改组件中的一些代码时,我都会在控制台中收到此消息:

[HMR] bundle rebuilding
client.js:207 [HMR] bundle rebuilt in 8218ms
process-update.js:27 [HMR] Checking for updates on the server...
process-update.js:81 [HMR] The following modules couldn't be hot updated: (Full reload needed)
This is usually because the modules which have changed (and their parents) do not know how to hot reload themselves. See http://webpack.github.io/docs/hot-module-replacement-with-webpack.html for more details.

谁有人偶然发现了这个?提前致谢!

编辑:这是我的客户端webpack配置:

const path = require('path');
const webpack = require('webpack');
const autoprefixer = require('autoprefixer');
const StyleLintPlugin = require('stylelint-webpack-plugin');
const notifier = require('node-notifier');

const configFileName = './.env.development.json';
let envConfig;

try {
  // eslint-disable-next-line import/no-dynamic-require, global-require
  envConfig = require(configFileName);
} catch (e) {
  envConfig = {};
}

const eslintSettings = {
  extends: path.join(__dirname, '.eslintrc.js'),
  configFile: path.join(__dirname, '.eslintrc.js'),
  emitWarning: true,
  cache: true,
};
const babelSettings = {
  extends: path.join(__dirname, '.babelrc'),
  cacheDirectory: true,
};

const excludes = [
  /node_modules(?![/\\]@local-package[/\\])/,
];
const roots = [
  path.join(__dirname, '../../node_modules'),
  path.join(__dirname, 'node_modules'),
  path.join(__dirname, 'client'),
];

const getCommonCSSLoaders = enableCSSModules => [
  {
    loader: 'style-loader',
  },
  {
    loader: 'css-loader',
    options: {
      modules: enableCSSModules,
      importLoaders: 1,
      localIdentName: '[name]_[local]_[hash:base64:3]',
    },
  },
  {
    loader: 'postcss-loader',
    options: {
      sourceMap: true,
      ident: 'postcss',
      plugins: () => [
        // eslint-disable-next-line global-require, import/no-extraneous-dependencies
        require('postcss-flexbugs-fixes'),
        autoprefixer({
          env: 'development',
          flexbox: 'no-2009',
        }),
      ],
    },
  },
];

const rules = [
  {
    enforce: 'pre',
    test: /\.js$/,
    exclude: /node_modules/,
    loader: 'eslint-loader',
    options: eslintSettings,
  },
  {
    test: /\.js$/,
    exclude: excludes,
    loader: 'babel-loader',
    options: babelSettings,
  },
  {
    test: /\.css$/,
    exclude: excludes,
    use: [
      ...getCommonCSSLoaders(true),
    ],
  },
  {
    test: /\.css$/,
    include: excludes,
    use: [
      ...getCommonCSSLoaders(false),
    ],
  },
  {
    test: /\.scss$/,
    exclude: excludes,
    use: [
      ...getCommonCSSLoaders(true),
      {
        loader: 'resolve-url-loader',
      },
      {
        loader: 'sass-loader',
        options: {
          sourceMap: true,
        },
      },
    ],
  },
  {
    test: /.*\.(eot|woff|woff2|ttf|svg|png|jpe?g|gif)$/i,
    use: [
      {
        loader: 'url-loader',
        options: {
          name: 'images/[name].[hash].[ext]',
          limit: 20000,
        },
      },
      {
        loader: 'image-webpack-loader',
        options: {
          mozjpeg: {
            quality: 80,
          },
          pngquant: {
            quality: '80-90',
          },
          bypassOnDebug: true,
        },
      },
    ],
  },
];

const plugins = [
  new webpack.LoaderOptionsPlugin({
    debug: true,
  }),
  new webpack.HotModuleReplacementPlugin(),
  new webpack.NoEmitOnErrorsPlugin(),
  new StyleLintPlugin({
    configFile: path.join(__dirname, '.stylelintrc.js'),
    files: [
      '**/*.s?(a|c)ss',
      '../shared/**/*.s?(a|c)ss',
    ],
    emitErrors: false,
  }),
  new webpack.NormalModuleReplacementPlugin(/\/components\/Bundles/, './components/AsyncBundles'),
  new webpack.NormalModuleReplacementPlugin(/\/Bundles/, './AsyncBundles'),
  new webpack.optimize.CommonsChunkPlugin({
    name: 'client',
    async: 'common',
    children: true,
    minChunks: (module, count) => {
      if (module.resource && (/^.*\.(css|scss)$/).test(module.resource)) {
        return false;
      }
      return count >= 3 && module.context && !module.context.includes('node_modules');
    },
  }),
  new webpack.optimize.CommonsChunkPlugin({
    name: 'client',
    children: true,
    minChunks: module => module.context && module.context.includes('node_modules'),
  }),
  new webpack.optimize.CommonsChunkPlugin({
    name: 'vendors',
    minChunks: module => module.context && module.context.includes('node_modules'),
  }),
  new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
  // eslint-disable-next-line func-names
  function () {
    this.plugin('done', (stats) => {
      notifier.notify({
        title: 'Webpack : Build Succeeded',
        message: `${stats.compilation.errors.length} Error(s) - ${stats.compilation.warnings.length} Warning(s)`,
      });
    });
    this.plugin('failed', () => {
      notifier.notify({
        title: 'Webpack',
        message: 'Build Failed HARD',
      });
    });
  },
];

const config = {
  name: 'client',
  target: 'web',
  devtool: 'inline-source-map',
  entry: ['webpack-hot-middleware/client', 'react-hot-loader/patch', './client/src/entry/js/polyfills', './client/src/entry/js/client'],
  output: {
    filename: 'client/[name].js',
    chunkFilename: 'client/chunks/[name].chunk.js',
    path: path.join(__dirname, 'public/dist'),
    publicPath: '/dist/',
    pathinfo: true,
  },
  module: {
    rules,
  },
  plugins,
  resolve: {
    modules: roots,
  },
  resolveLoader: {
    modules: roots,
  },
  node: {
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
  },
};

module.exports = config;

1 回答

  • 1

    也许它与你的webpack.config文件有关?你在参赛作品中设置了热门的东西吗?

    const config = {
        entry: [
            'babel-polyfill',
    
            'react-hot-loader/patch', // activate HMR for React
            `webpack-hot-middleware/client?path=http://${HOST}:${PORT}/__webpack_hmr`, // bundle the client for webpack-hot-middleware and connect to the provided endpoint
    
            './src/client.jsx',
        ],
    

    我有更好的运气使用Hapi作为我的服务器与webpack-hot-middleware和webpack-dev-middleware . 这是一个片段

    import Webpack from 'webpack';
    import HapiWebpackPlugin from 'hapi-webpack-plugin';
    
    const config = require('../../../webpack.config.js'); // eslint-disable-line global-require
    const compiler = Webpack(config);
    
    const options = {
        assets: {
            // webpack-dev-middleware options - https://github.com/webpack/webpack-dev-middleware
            index: '/public/index.html',
        },
        hot: {
            // webpack-hot-middleware options - https://github.com/glenjamin/webpack-hot-middleware
        },
        compiler,
    };
    
    server.register({
        register: HapiWebpackPlugin,
        options,
    }, (error) => {
        if (error) {
            console.error(error);
        }
    });
    

    如果你想尝试Hapi,请查看我的hapi-react-hot-loader-example

    这是我的完整webpack.config

    const path = require('path');
    const webpack = require('webpack');
    const CopyWebpackPlugin = require('copy-webpack-plugin');
    const ExtractTextPlugin = require('extract-text-webpack-plugin');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const ProgressBarPlugin = require('progress-bar-webpack-plugin');
    const HtmlWebpackHarddiskPlugin = require('html-webpack-harddisk-plugin');
    const RobotstxtPlugin = require('robotstxt-webpack-plugin').default;
    
    const PORT = process.env.PORT || 3000;
    const HOST = process.env.HOST || 'localhost';
    const NODE_ENV = process.env.NODE_ENV || 'production';
    const isProduction = (NODE_ENV === 'production');
    const isDevelopment = (NODE_ENV === 'development');
    
    const config = {
        entry: isDevelopment
            ? [
                'babel-polyfill',
    
                'react-hot-loader/patch', // activate HMR for React
                `webpack-hot-middleware/client?path=http://${HOST}:${PORT}/__webpack_hmr`, // bundle the client for webpack-hot-middleware and connect to the provided endpoint
    
                './src/client.jsx',
            ]
            : [
                'babel-polyfill',
    
                './src/client.jsx',
            ],
    
        resolve: {
            extensions: ['.js', '.jsx', '.json'],
        },
    
        output: {
            path: path.join(__dirname, 'dist/public/'),
            filename: isDevelopment
                ? 'main.js'
                : 'assets/scripts/[name].[chunkhash].js',
        },
    
        module: {
            rules: [
                {
                    test: /\.css$/,
                    use: ['css-hot-loader'].concat(
                        ExtractTextPlugin.extract({
                            fallback: 'style-loader',
                            use: [
                                {
                                    loader: 'css-loader',
                                    options: {minimize: true},
                                },
                                {
                                    loader: 'postcss-loader',
                                },
                            ],
                        })
                    ),
                },
                {
                    test: /\.jsx?$/,
                    use: ['babel-loader'],
                    include: path.join(__dirname, 'src'),
                },
            ],
        },
    
        plugins: [
            new ProgressBarPlugin(),
    
            new webpack.DefinePlugin({
                'process.env.NODE_ENV': JSON.stringify(NODE_ENV),
            }),
    
            isDevelopment
                ? null
                : new webpack.optimize.ModuleConcatenationPlugin(),
    
            isDevelopment
                ? new webpack.HotModuleReplacementPlugin() // enable HMR globally
                : null,
            isDevelopment
                ? new webpack.NamedModulesPlugin() // prints more readable module names in the browser console on HMR updates
                : null,
            isDevelopment
                ? new webpack.NoEmitOnErrorsPlugin() // do not emit compiled assets that include errors
                : null,
    
            new ExtractTextPlugin({
                filename: isDevelopment
                    ? 'assets/styles/main.css'
                    : 'assets/styles/[name].[chunkhash].css',
            }),
    
            isDevelopment
                ? null
                : new webpack.optimize.CommonsChunkPlugin({
                    name: 'vendor',
                    minChunks: module => /node_modules/.test(module.resource),
                }),
    
            isDevelopment
                ? null
                : new webpack.optimize.CommonsChunkPlugin({name: 'manifest'}),
    
            isProduction
                ? new webpack.optimize.UglifyJsPlugin()
                : null,
    
            new HtmlWebpackPlugin({
                template: path.resolve(__dirname, 'src/index.html'),
                minify: isProduction ? {collapseWhitespace: true, collapseInlineTagWhitespace: true} : false,
                alwaysWriteToDisk: true,
            }),
            new HtmlWebpackHarddiskPlugin(),
    
            new CopyWebpackPlugin([
                {
                    context: 'src/assets/media',
                    from: '**/*',
                    to: 'assets/media',
                },
            ]),
    
            new RobotstxtPlugin({
                policy: [
                    isProduction
                        ? {userAgent: '*', allow: '/'}
                        : {userAgent: '*', disallow: '/'},
                ],
            }),
        ].filter(Boolean),
    
        devtool: isProduction
            ? 'none'
            : 'cheap-module-eval-source-map',
    
        performance: {
            maxAssetSize: 500000,
        },
    };
    
    module.exports = config;
    

相关问题