如何在Webpack 2和webpackDevMiddleware中使用react-hot-loader?

3

我正在使用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@3react-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。 logUpdates @ bundle.js:29964 applyCallback @ bundle.js:29932 (anonymous) @ bundle.js:29940 bundle.js:29972 [HMR] - ./client/components/CrawlForm.jsx
怎样才能使它起作用呢?
注:CSS 热加载正常工作,所以我已经解决了这部分问题。
4个回答

2

我在将运行时错误的错误叠加层渲染出来和从中恢复方面遇到了一些问题。我注意到当发生错误时,webpack-dev-server会进行完全重新加载。

以下代码片段可以模拟这种情况:

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

我的react-hot-boilerplate分支已经上传至Github。


2
我花了几天时间最终解决了这个问题。以下是我可行的代码: 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。另一个重要的方面是在我的<Router />组件中添加key={Math.random()}

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'));
  });
}

祝你好运


哇...这比以前复杂多了。<AppContainer>if(module.hot)部分对我有用。谢谢! - mpen

2

以下是从Dan Abramov处改编并借鉴了realseanp的代码,完整的指令如下:

  1. yarn add react-hot-loader@3
  2. Update webpack.config.js:
    1. Add react-hot-loader/patch and webpack-hot-middleware/client to top of your entry
    2. Add react-hot-loader/babel to your babel-loader plugins
    3. Add new HotModuleReplacementPlugin() to your webpack plugins
  3. Add webpack-dev-middleware and webpack-hot-middlware to express:

    // 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. Add <AppContainer> and react.hot to your client entry point:

    // 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);
        });
    }
    

0

我也将每个点添加到我的应用程序中,但它没有起作用。 问题出在我的webpack.config.js文件中的publicPath。 我有

publicPath: '/client/dist'

然后我改成了

publicPath: '/'

现在它可以工作了


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接