热模块替换 - 更新但不重新渲染

11

我正在运行一个 Express 服务器,作为我的 React 应用程序的 API,而该应用程序由 webpack-dev-server 打包和提供服务。

我正在尝试使热模块替换工作,几乎成功了,但当我更改文件时,控制台会显示以下内容:

HMR 的控制台输出

然而,除非手动刷新,否则应用程序不会重新渲染。不确定这是否相关,但当我更新我的 .scss 文件时,它会在没有手动操作的情况下刷新,并且按照我所期望的进行更新。

版本信息:

"webpack": "2.1.0-beta.22"

"webpack-dev-server": "2.1.0-beta.8"

"react-hot-loader": "3.0.0-beta.5"

我尝试使用最新的 webpack 版本,但遇到了无法克服的验证错误。

我通过以下方式运行 webpack:"webpack": "webpack-dev-server --port 4000 --env.dev",而我的 Express 服务器在 http://localhost:3000 上运行。

这是我的 webpack.config.babel.js

const webpack = require('webpack');
const { resolve, join } = require('path');
const { getIfUtils, removeEmpty } = require('webpack-config-utils')

const getEntry = (ifDev) => {
  let entry

  if (ifDev) {
    entry = {
      app: [
        'react-hot-loader/patch',
        'webpack/hot/dev-server',
        'webpack-dev-server/client?http://localhost:4000/',
        './js/index.js'
      ],
      vendor: ['react']
    }
  } else {
    entry = {
      bundle: './js/index.js',
      vendor: ['react']
    }
  }

  return entry
}

const config = env => {
  const { ifProd, ifDev } = getIfUtils(env)

  return {
    entry: getEntry(ifDev),
    output: {
      path: resolve('./public/dist/'),
      publicPath: 'http://localhost:4000/',
      filename: '[name].bundle.js',
    },
    context: resolve(__dirname, 'assets'),
    devtool: env.prod ? 'source-map' : 'eval',
    devServer: {
      contentBase: resolve('./public/dist/'),
      headers: { 'Access-Control-Allow-Origin': '*' },
      publicPath: 'http://localhost:4000/',
      hot: true,
      noInfo: true,
      inline: true
    },
    bail: env.prod,
    module: {
      loaders: [
        { test: /\.scss$/, loaders: [ 'style', 'css', 'sass' ], exclude: /node_modules|lib/ },
        { test: /\.(js|jsx)$/, exclude: /node_modules/, loaders: [ 'babel-loader' ] },
        { test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/, loader: 'file-loader' }
      ]
    },
    resolve: {
      extensions: ['.js', '.jsx']
    },
    plugins: removeEmpty([
      ifDev(new webpack.NoErrorsPlugin()),
      ifDev(new webpack.NamedModulesPlugin()),
      ifDev(new webpack.HotModuleReplacementPlugin()),

      new webpack.DefinePlugin({
        'process.env': { NODE_ENV: JSON.stringify((env.prod) ? 'production' : 'development') }
      }),
      new webpack.optimize.CommonsChunkPlugin({
        name: 'vendor',
        minChunks: Infinity,
        filename: 'vendor.bundle.js'
      }),

      ifProd(new webpack.LoaderOptionsPlugin({
        minimize: true,
        debug: false
      })),
      ifProd(new webpack.optimize.UglifyJsPlugin({
        compress: { warnings: false },
        output: { comments: false },
        sourceMap: false
      }))
    ]),
  }
}

module.exports = config

这是我的.babelrc文件,我在其中调用了react-hot-loader

{
  "presets": [["es2015", { modules: false }], "stage-0", "react"],
  "plugins": ["react-hot-loader/babel"],
  "env": {
    "test": {
      "plugins": ["istanbul"],
      "presets": ["es2015", "stage-0", "react"]
    }
  },
  "sourceMaps": "inline"
}
3个回答

6
使用React Hot Loader v3和Babel转换,您需要在组件的根部(即您进行渲染或创建Redux提供程序的位置)执行以下操作:
render(
  <AppContainer>
    <Root
      store={ store }
    />
  </AppContainer>,
  document.getElementById('root')
);

if (module.hot) {
  module.hot.accept('./containers/Root', () => {
    const RootContainer = require('./containers/Root').default;
    render(
      <AppContainer>
        <RootContainer
          store={ store }
        />
      </AppContainer>,
      document.getElementById('root')
    );
  });
}

在新版本的Hot Loader中,您必须使用module.hot.accept明确接受热更新。

在更复杂的Redux项目中(具有路由和热重新加载reducer),您可以这样做:

/**
 * Starts the React app with the Router, and renders it to the given DOM container
 * @param  {DOMElement} container
 */
export default function app(container) {
  const store = createStore(
    combineReducers({
      ...reducers,
      routing: routerReducer,
      form: formReducer,
    }),
    compose(
      applyMiddleware(
        routerMiddleware(hashHistory),
        thunkMiddleware,
        promiseMiddleware
      ),
      process.env.NODE_ENV !== 'production' && window.devToolsExtension ? window.devToolsExtension() : (param) => param
    )
  );

  if (module.hot) {
    module.hot.accept('./reducers', () => {
      const nextReducers = require('./reducers');
      const nextRootReducer = combineReducers({
        ...nextReducers,
        routing: routerReducer,
        form: formReducer,
      });
      store.replaceReducer(nextRootReducer);
    });
  }

  const history = syncHistoryWithStore(hashHistory, store);

  render({ store, history, container });

  store.dispatch(loadEventsWhenLoggedIn());

  if (module.hot) {
    module.hot.accept('./render', () => {
      const newRender = require('./render').default;
      newRender({ store, history, container });
    });
  }
}

(and render.js)

/**
 * Starts the React app with the Router, and renders it to the given DOM container
 * @param  {DOMElement} container
 */
export default function render({ store, history, container }) {
  ReactDOM.render(
    <Provider store={store}>
      <div className='container'>
        <Routes history={history} store={store} />
      </div>
    </Provider>,
    container
  );
}

如果想了解更多实例,可以查看Dan Abramov的Redux devtools示例仓库,例如这个文件:https://github.com/gaearon/redux-devtools/blob/master/examples/todomvc/index.js


2

HMR对于CSS的工作是自动的,因为style-loader已经支持了这个功能。

但是在React中不是这样的。你需要使用react-hot-loader

通过npm安装它,并将以下内容更改为:

{ test: /\.(js|jsx)$/, exclude: /node_modules/, loaders: [ 'babel-loader' ] },

致:

{ test: /\.(js|jsx)$/, exclude: /node_modules/, loaders: [ 'react-hot-loader', 'babel-loader' ] },

如果您想了解更多信息,我建议阅读 "Webpack的HMR和React-Hot-Loader——缺失的手册"。


1
我忘记在调用 react-hot-loader 的地方添加我的 .babelrc 文件,在 3.X 版本中,它鼓励你将其从配置文件中移动到 babel 中。 - speak

0
我遇到了这个问题,添加了module.hot.accept()函数,但它仍然无法正常工作。
hmr [connected]...但没有热替换。
*删除了node_module、package-lock.json、dist/build文件夹...
*更改了webpack.config.client.production.js --> 添加了
    plugins: [
        new webpack.ProvidePlugin({
            process: "process/browser"
        })
    ],
    resolve:{
        alias:{
            process: "process/browser"
        }
    }

然后执行 npm install --save-dev process,

import process from 'process' 添加到打包入口文件(对我来说是 main.js)中。

...

npm install

然后运行它。


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