如何让 react-hot-loader 与 webpack 2 和 webpackDevMiddleware 一起工作?

Posted

技术标签:

【中文标题】如何让 react-hot-loader 与 webpack 2 和 webpackDevMiddleware 一起工作?【英文标题】:How to get react-hot-loader working with webpack 2 and 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 热加载工作得很好,所以我得到了那部分工作。

【问题讨论】:

【参考方案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 的部分代码,完整说明如下:

    yarn add react-hot-loader@3 更新webpack.config.js
      react-hot-loader/patchwebpack-hot-middleware/client 添加到entry 的顶部 将react-hot-loader/babel 添加到您的babel-loader pluginsnew HotModuleReplacementPlugin() 添加到您的 webpack 插件中

    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`);
    );
    

    &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: '/'

现在它可以工作了

【讨论】:

以上是关于如何让 react-hot-loader 与 webpack 2 和 webpackDevMiddleware 一起工作?的主要内容,如果未能解决你的问题,请参考以下文章

无法让多应用 webpack 配置与 react-hot-loader 一起使用

react-hot-loader 与外部 configureStore 使用 redux-saga 抛出“regeneratorRuntime 未定义”

无法使 react-hot-loader 和 webpack-dev-server 与 react-router 一起工作

使用 react-hot-loader 包时如何自动删除 *.hot-update.json 文件?

无法解析 react-hot-loader/patch

react-hot-loader: 未检测到 react-????-dom 补丁