【问题标题】:How to get react-hot-loader working with webpack 2 and webpackDevMiddleware?如何让 react-hot-loader 与 webpack 2 和 webpackDevMiddleware 一起工作?
【发布时间】:2017-07-06 07:04:56
【问题描述】:

我使用的是 express 中间件而不是 webpack-dev-server:

const config = require("../webpack.config.js");

if(process.env.NODE_ENV === 'development') {
    const webpack = require('webpack');
    const webpackDevMiddleware = require('webpack-dev-middleware');
    const webpackHotMiddleware = require('webpack-hot-middleware');
    const compiler = webpack(config);

    app.use(webpackDevMiddleware(compiler, {
        stats: {colors: true},
    }));
    app.use(webpackHotMiddleware(compiler));
}

我已经从react-hot-loader@3 尝试了react-hot-loader/patchreact-hot-loader/babelreact-hot-loader/webpack

module.exports = {
    context: path.join(__dirname, 'client'),
    entry: [
        'webpack-hot-middleware/client',
        'react-hot-loader/patch',
        './entry.less',
        './entry',
    ],
    output: {
        path: path.join(__dirname, 'public'),
        filename: 'bundle.js',
        publicPath: '/',
    },
    module: {
        rules: [
            {
                test: /\.jsx/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: {
                            plugins: ['transform-react-jsx', 'transform-class-properties', 'react-hot-loader/babel'],
                        },
                    },
                    'react-hot-loader/webpack'
                ],
            },

但它们似乎都不起作用。我刚刚收到此错误消息:

[HMR] 以下模块无法热更新:(需要完全重新加载) 这通常是因为已更改的模块(及其父模块)不知道如何自行热重载。有关详细信息,请参阅http://webpack.github.io/docs/hot-module-replacement-with-webpack.html。 日志更新@bundle.js:29964 应用回调@bundle.js:29932 (匿名)@ bundle.js:29940 bundle.js:29972
[HMR] - ./client/components/CrawlForm.jsx

让它发挥作用的诀窍是什么?

注意CSS 热加载工作得很好,所以我得到了那部分工作。

【问题讨论】:

    标签: reactjs webpack react-hot-loader


    【解决方案1】:

    我花了好几天才终于破案。这是我的有效代码:

    Webpack 配置对象

    const clientConfig = {
      entry: {
        client: [
          'react-hot-loader/patch',
          'webpack-hot-middleware/client',
          'babel-polyfill',
          './src/client/client.js',
        ],
      },
      output: {
        path: path.resolve(__dirname, './build/public'),
        filename: '[name].js',
        publicPath: '/',
      },
      devtool: 'inline-source-map',
      plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoEmitOnErrorsPlugin(),
        new webpack.LoaderOptionsPlugin({
          debug: true,
        }),
        new CopyWebpackPlugin([
          { from: './src/assets/fonts', to: 'fonts' },
          { from: './src/assets/images', to: 'images' },
        ]),
        new webpack.EnvironmentPlugin(['GOOGLE_MAP_API_KEY']),
      ],
      module: {
        rules: [
          {
            test: /(\.js|\.jsx)$/,
            loader: 'babel-loader',
            exclude: /node_modules/,
            options: {
              presets: [['es2015', { loose: true }], 'react', 'stage-2'],
            },
          },
          {
            test: /\.scss$/,
            use: [
              'style-loader',
              'css-loader',
              'sass-loader',
            ],
          },
        ],
      },
    };
    

    服务器 index.js

    我和你一样使用开发中间件和热中间件。我还从 react-hot-loader 导入 AppContainer 并包装我的组件。

    import express from 'express';
    import React from 'react';
    import routes from 'components/Routes';
    import html from './html';
    import { renderToString } from 'react-dom/server';
    import { match, RouterContext } from 'react-router';
    import { Provider } from 'react-redux';
    import makeStore from 'store';
    import Immutable from 'immutable';
    import setupNameless from './setupNameless';
    import db from './database';
    import { actions } from '../client/constants';
    import webpack from 'webpack';
    import webpackHotMiddleware from 'webpack-hot-middleware';
    import webpackDevMiddleware from 'webpack-dev-middleware';
    import { clientConfig as wpConfig } from '../../webpack.config.js';
    import { AppContainer } from 'react-hot-loader';
    import dotenv from 'dotenv';
    
    dotenv.config();
    
    const compiler = webpack(wpConfig);
    
    db();
    
    const app = express();
    app.use(webpackDevMiddleware(compiler, {
      publicPath: wpConfig.output.publicPath,
      // noInfo: true,
      stats: {
        colors: true,
      },
    }));
    app.use(webpackHotMiddleware(compiler));
    app.use(express.static('build/public'));
    
    const { commander: nameless, apiPrefix } = setupNameless(app);
    
    app.use((req, res, next) => {
      // make DB call here to fetch jobs.
      nameless.exec('jobs', actions.GET_JOBS).then((jobs) => {
    
        const store = makeStore(Immutable.fromJS({
          // filters: {},
          app: {
            apiPrefix,
            search: {
              query: '',
              options: {},
            },
          },
          jobs,
        }));
    
        match({
          routes,
          location: req.originalUrl,
        }, (error, redirectLocation, renderProps) => {
          if (error) {
            res.status(500).send(error.message);
          } else if (redirectLocation) {
            res.redirect(302, redirectLocation.pathname + redirectLocation.search);
          } else if (renderProps) {
            // You can also check renderProps.components or renderProps.routes for
            // your "not found" component or route respectively, and send a 404 as
            // below, if you're using a catch-all route.
            try {
              res.status(200).send(html(renderToString(
                <AppContainer>
                  <Provider store={store}>
                    <RouterContext {...renderProps} />
                  </Provider>
                </AppContainer>
              ), store.getState()));
            } catch (err) {
              next(err);
            }
          } else {
            res.status(404).send('Not found');
          }
        });
      }, (e) => {
        next(e);
      }).catch(e => {
        next(e);
      });
    });
    
    app.use(logErrors);
    
    function logErrors(err, req, res, next) {
      console.error(err.stack);
      next(err);
    }
    
    app.listen(process.env.PORT || 3000, () => {
      console.log(`App listening on port ${process.env.PORT || 3000}`);
    });
    

    Client.js

    这就是让它发挥作用的魔力。我必须添加 if (module.hot) 代码并从 react-hot-loader 导入 AppContainer。另一个重要方面是将key={Math.random()} 添加到我的&lt;Router /&gt; 组件中。

    import { match, Router, browserHistory as history } from 'react-router';
    import routes from './components/Routes';
    import ReactDOM from 'react-dom';
    import React from 'react';
    import { Provider } from 'react-redux';
    import makeStore from './store';
    import Immutable from 'immutable';
    import createLogger from 'redux-logger';
    import createSagaMiddleware from 'redux-saga';
    import sagas from './sagas';
    import { AppContainer } from 'react-hot-loader';
    
    const logger = createLogger();
    const sagaMiddleware = createSagaMiddleware();
    
    const store = makeStore(
        Immutable.fromJS(window.__INITIAL_STATE__),
        logger,
        sagaMiddleware
    );
    
    sagaMiddleware.run(sagas);
    
    ReactDOM.render(
      <AppContainer>
        <Provider store={store}>
          <Router history={history} routes={routes} />
        </Provider>
      </AppContainer>,
      document.getElementById('app'));
    
    if (module.hot) {
      module.hot.accept('./components/Routes', () => {
        const nextRoutes = require('./components/Routes').default;
        ReactDOM.render(
          <AppContainer>
            <Provider store={store}>
              <Router key={Math.random()} history={history} routes={nextRoutes} />
            </Provider>
          </AppContainer>,
          document.getElementById('app'));
      });
    }
    

    祝你好运?

    【讨论】:

    • 哇...这比以前复杂多了。 &lt;AppContainer&gt;if(module.hot) 位对我有用。谢谢!!
    【解决方案2】:

    转述Dan Abramov 并借用realseanp 的部分代码,完整说明如下:

    1. yarn add react-hot-loader@3
    2. 更新webpack.config.js
      1. react-hot-loader/patchwebpack-hot-middleware/client 添加到entry 的顶部
      2. react-hot-loader/babel 添加到您的babel-loader plugins
      3. new HotModuleReplacementPlugin() 添加到您的 webpack 插件中
    3. webpack-dev-middlewarewebpack-hot-middlware表示:

      // server/entry.jsx
      const express = require('express');
      const path = require('path');
      const cons = require('consolidate');
      const fs = require('fs');
      
      const port = 5469;
      const app = express();
      
      app.disable('x-powered-by');
      app.engine('hbs', cons.handlebars);
      app.set('view engine', 'hbs');
      app.set('views', path.join(__dirname, '../views'));
      
      const wpConfig = require("../webpack.config.js");
      
      if(process.env.NODE_ENV === 'development') {
          const webpack = require('webpack');
          const webpackDevMiddleware = require('webpack-dev-middleware');
          const webpackHotMiddleware = require('webpack-hot-middleware');
          const compiler = webpack(wpConfig);
      
          app.use(webpackDevMiddleware(compiler, {
              stats: {colors: true},
          }));
          app.use(webpackHotMiddleware(compiler));
      }
      
      app.use(require('./routes'));
      
      app.use(express.static(wpConfig.output.path));
      
      app.listen(port, function () {
          console.log(`Listening on http://localhost:${port}`);
      });
      
    4. &lt;AppContainer&gt;react.hot 添加到您的客户端入口点:

      // client/entry.jsx
      import ReactDOM from 'react-dom';
      import App from './components/App';
      import { AppContainer } from 'react-hot-loader';
      
      function render(Root) {
          ReactDOM.render(<AppContainer><Root/></AppContainer>, document.getElementById('react-root'));
      }
      
      render(App);
      
      if(module.hot) {
          module.hot.accept('./components/App', () => {
              render(require('./components/App').default);
          });
      }
      

    【讨论】:

      【解决方案3】:

      我在获取错误叠加层以呈现运行时错误以及从中恢复时遇到了一些麻烦。我注意到webpack-dev-server 在发生错误时会完全重新加载。

      这可以用下面的sn-p来模拟:

      if (module.hot) module.hot.accept('./App', () => {
        try {
          render(App)
        } catch (e) {
          location.reload();
        }
      });
      

      我的工作 fork of react-hot-boilerplate 可以在 Github 上找到。

      【讨论】:

        【解决方案4】:

        我还在我的应用程序中添加了每个点,但它没有用 问题出在我的webpack.config.js 中的publicPath 我有

        publicPath: '/client/dist'
        

        然后我改成

        publicPath: '/'
        

        现在它可以工作了

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-02-11
          • 2016-08-20
          • 2016-02-28
          • 2017-11-03
          • 2017-11-13
          • 1970-01-01
          • 2017-06-27
          • 2017-12-06
          相关资源
          最近更新 更多