热模块更换 - 更新但不重新渲染
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
【讨论】:
以上是关于热模块更换 - 更新但不重新渲染的主要内容,如果未能解决你的问题,请参考以下文章