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

Posted

技术标签:

【中文标题】热模块更换 - 更新但不重新渲染【英文标题】:Hot module replacement - Updating but not re-rendering 【发布时间】:2017-02-24 19:00:29 【问题描述】:

我正在运行一个快速服务器,它将充当我的 React 应用程序的 API,该应用程序由 webpack-dev-server 捆绑和提供服务。

我正在尝试让热模块更换工作,并且快到了,当我对我的文件进行更改时,我在控制台中得到了这个:

但应用永远不会重新渲染,除非手动刷新。不知道这是否相关,但是当我更新我的 .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-dev-server --port 4000 --env.dev" 运行 webpack,而我的 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"

【问题讨论】:

【参考方案1】:

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’s HMR & React-Hot-Loader — The Missing Manual”。

【讨论】:

我忘了在3.X 中添加我的.babelrc,我在其中调用react-hot-loader,它鼓励您将其从配置移至babel。【参考方案2】:

使用 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 项目中(使用路由和热重载减速器),您可以执行以下操作:

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

(和 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 示例 repo,例如此文件:https://github.com/gaearon/redux-devtools/blob/master/examples/todomvc/index.js

【讨论】:

以上是关于热模块更换 - 更新但不重新渲染的主要内容,如果未能解决你的问题,请参考以下文章

RN 热更新

RN热更新

以下模块无法热更新:(需要完全重新加载)

热更新

Unity热更新技术整理

Unity热更新技术整理