react-hot-loader 和 webpack-dev-server 不重新加载更改
react-hot-loader and webpack-dev-server don't reload changes
我试着按照 react-hot-loader/getstarted 起床并在我的 webpack 配置中 运行 react-hot-loader。当我对我的组件进行一些更改时,react-hot-loader 不会重新加载更改。我更新了 npm 脚本,将 react-hot-loader 添加到 babel 和 webpack 配置中,并将 module.hot.accept
添加到 index.jsx
.
package.json
"scripts": {
"dev": "webpack-dev-server -d --progress --colors --port 8090 --hot --inline",
},
"dependencies": {
"aphrodite": "^2.2.1",
"bootstrap": "^4.1.1",
"bootstrap-select": "^1.13.1",
"classnames": "^2.2.5",
"d3": "^3.5.17",
"fixed-data-table-2": "^0.8.13",
"font-awesome": "^4.7.0",
"jquery": "^3.3.1",
"js-cookie": "^2.2.0",
"lodash": "^4.17.10",
"moment": "^2.22.1",
"popper.js": "^1.14.3",
"react": "^16.3.2",
"react-bootstrap-typeahead": "^3.1.3",
"react-datetime": "^2.14.0",
"react-dom": "^16.3.2",
"react-hot-loader": "^4.3.3",
"react-loadable": "^5.4.0",
"react-modal": "^3.4.4",
"react-redux": "^5.0.7",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-router-redux": "^5.0.0-alpha.9",
"react-slidedown": "^1.3.0",
"react-tippy": "^1.2.2",
"react-toastify": "^4.0.1",
"react-transition-group": "^2.3.1",
"redux": "^4.0.0",
"redux-saga": "^0.16.0"
},
"devDependencies": {
"autoprefixer": "^8.4.1",
"babel": "^6.23.0",
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.3",
"babel-loader": "^7.1.4",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-plugin-transform-async-to-generator": "^6.24.1",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.6.1",
"babel-preset-latest": "^6.24.1",
"babel-preset-react": "^6.24.1",
"css-loader": "^0.28.11",
"eslint": "^4.19.1",
"eslint-config-airbnb": "^16.1.0",
"eslint-plugin-import": "^2.11.0",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-react": "^7.7.0",
"file-loader": "^1.1.11",
"http-server": "^0.11.1",
"less": "^3.0.2",
"less-loader": "^4.1.0",
"node-sass": "^4.9.0",
"npm-install-webpack-plugin": "^4.0.5",
"postcss": "^6.0.22",
"postcss-loader": "^2.1.4",
"redux-devtools-extension": "^2.13.2",
"sass-loader": "^7.0.1",
"sass-resources-loader": "^1.3.3",
"style-loader": "^0.21.0",
"url-loader": "^1.0.1",
"webpack": "^4.6.0",
"webpack-cli": "^2.1.2",
"webpack-dev-server": "^3.1.4",
"webpack-merge": "^4.1.2",
"webpack-notifier": "^1.6.0"
}
.babelrc
{
"presets": [
[
"latest", {
"es2015": {
"modules": false
}
}
],
"react"
],
"plugins": [
"transform-object-rest-spread",
"transform-class-properties",
"syntax-dynamic-import",
"transform-async-to-generator",
"react-hot-loader/babel"
]
}
webpack.config.js
const webpack = require('webpack');
const webpackMerge = require('webpack-merge');
const path = require('path');
const WebpackNotifierPlugin = require('webpack-notifier');
const TARGET = process.env.npm_lifecycle_event;
console.log(`target event is ${TARGET}`);
let outputFileName = 'app';
outputFileName += TARGET === 'prod' ? '.min.js' : '.js';
const common = {
entry: ['babel-polyfill', 'react-hot-loader/patch', './index.jsx'],
output: {
publicPath: '/',
},
module: {
rules: [
{
test: /\.js[x]?$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
},
},
],
},
plugins: [
new webpack.ProvidePlugin({
jQuery: 'jquery',
$: 'jquery',
jquery: 'jquery',
'window.jQuery': 'jquery',
}),
new WebpackNotifierPlugin(),
],
resolve: {
modules: [
path.resolve('.'),
path.resolve('script'),
path.resolve('script', 'views'),
'node_modules',
],
extensions: ['.js', '.jsx', '.json'],
},
};
if (TARGET === 'dev' || !TARGET) {
module.exports = webpackMerge(common, {
devtool: 'eval-source-map',
output: {
filename: 'bundle.js',
sourceMapFilename: '[file].map',
},
module: {
rules: [
{
test: /\.scss$/,
loaders: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
config: {
path: 'postcss.config.js',
},
},
},
'sass-loader',
],
},
{
test: /\.less$/,
loaders: ['style-loader', 'css-loader', 'less-loader'],
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(eot|ttf|svg|gif|png|jpg|otf|woff|woff2)$/,
loader: 'url-loader',
},
],
},
devServer: {
contentBase: path.resolve(__dirname), // New
historyApiFallback: true,
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development'),
}),
new webpack.HotModuleReplacementPlugin(),
],
});
}
index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import createHistory from 'history/createBrowserHistory';
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { ConnectedRouter, routerReducer, routerMiddleware } from 'react-router-redux';
import { Provider } from 'react-redux';
import { AppContainer } from 'react-hot-loader';
import createSagaMiddleware from 'redux-saga';
import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly';
import 'bootstrap';
import 'styles/custom.scss';
import App from 'App';
import reducers from 'state';
import sagas from 'sagas';
const history = createHistory();
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
combineReducers({
...reducers,
router: routerReducer,
}),
composeWithDevTools(applyMiddleware(routerMiddleware(history), sagaMiddleware)),
);
if (module.hot) {
module.hot.accept('state', () => {
store.replaceReducer(require('state').default);
});
}
sagaMiddleware.run(sagas);
const render = (Component) => {
ReactDOM.render(
<AppContainer>
<Provider store={store}>
<ConnectedRouter history={history}>
<Component />
</ConnectedRouter>
</Provider>
</AppContainer>,
document.getElementById('app'),
);
};
render(App);
if (module.hot) {
module.hot.accept('App', () => { render(App); });
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Test</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
</head>
<body>
<div id="app">
</div>
<script type="text/javascript" src="/bundle.js"></script>
</body>
</html>
- 您可以使用
react-script
和 react-app-rewire-hot-loader
你可能不会用react-hot-loader/babel
{ test: /\.(js|jsx)$/,
include: paths.appSrc,
loader: require.resolve("babel-loader"),
options: {
// This is a feature of babel-loader for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
plugins: ["react-hot-loader/babel"]
},
}
您是否尝试过将根组件导出为热组件:
import { hot } from "react-hot-loader"
const App = () => <h3>Hello World</h3>
export default hot(module)(App)
您定义了 Hot Module Replacement 两次。您在 package.json
中定义了 --hot
参数,然后在 webpack.config.js
中定义了 new webpack.HotModuleReplacementPlugin()
。你应该只定义一次。
我试着按照 react-hot-loader/getstarted 起床并在我的 webpack 配置中 运行 react-hot-loader。当我对我的组件进行一些更改时,react-hot-loader 不会重新加载更改。我更新了 npm 脚本,将 react-hot-loader 添加到 babel 和 webpack 配置中,并将 module.hot.accept
添加到 index.jsx
.
package.json
"scripts": {
"dev": "webpack-dev-server -d --progress --colors --port 8090 --hot --inline",
},
"dependencies": {
"aphrodite": "^2.2.1",
"bootstrap": "^4.1.1",
"bootstrap-select": "^1.13.1",
"classnames": "^2.2.5",
"d3": "^3.5.17",
"fixed-data-table-2": "^0.8.13",
"font-awesome": "^4.7.0",
"jquery": "^3.3.1",
"js-cookie": "^2.2.0",
"lodash": "^4.17.10",
"moment": "^2.22.1",
"popper.js": "^1.14.3",
"react": "^16.3.2",
"react-bootstrap-typeahead": "^3.1.3",
"react-datetime": "^2.14.0",
"react-dom": "^16.3.2",
"react-hot-loader": "^4.3.3",
"react-loadable": "^5.4.0",
"react-modal": "^3.4.4",
"react-redux": "^5.0.7",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-router-redux": "^5.0.0-alpha.9",
"react-slidedown": "^1.3.0",
"react-tippy": "^1.2.2",
"react-toastify": "^4.0.1",
"react-transition-group": "^2.3.1",
"redux": "^4.0.0",
"redux-saga": "^0.16.0"
},
"devDependencies": {
"autoprefixer": "^8.4.1",
"babel": "^6.23.0",
"babel-core": "^6.26.3",
"babel-eslint": "^8.2.3",
"babel-loader": "^7.1.4",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-plugin-transform-async-to-generator": "^6.24.1",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.6.1",
"babel-preset-latest": "^6.24.1",
"babel-preset-react": "^6.24.1",
"css-loader": "^0.28.11",
"eslint": "^4.19.1",
"eslint-config-airbnb": "^16.1.0",
"eslint-plugin-import": "^2.11.0",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-react": "^7.7.0",
"file-loader": "^1.1.11",
"http-server": "^0.11.1",
"less": "^3.0.2",
"less-loader": "^4.1.0",
"node-sass": "^4.9.0",
"npm-install-webpack-plugin": "^4.0.5",
"postcss": "^6.0.22",
"postcss-loader": "^2.1.4",
"redux-devtools-extension": "^2.13.2",
"sass-loader": "^7.0.1",
"sass-resources-loader": "^1.3.3",
"style-loader": "^0.21.0",
"url-loader": "^1.0.1",
"webpack": "^4.6.0",
"webpack-cli": "^2.1.2",
"webpack-dev-server": "^3.1.4",
"webpack-merge": "^4.1.2",
"webpack-notifier": "^1.6.0"
}
.babelrc
{
"presets": [
[
"latest", {
"es2015": {
"modules": false
}
}
],
"react"
],
"plugins": [
"transform-object-rest-spread",
"transform-class-properties",
"syntax-dynamic-import",
"transform-async-to-generator",
"react-hot-loader/babel"
]
}
webpack.config.js
const webpack = require('webpack');
const webpackMerge = require('webpack-merge');
const path = require('path');
const WebpackNotifierPlugin = require('webpack-notifier');
const TARGET = process.env.npm_lifecycle_event;
console.log(`target event is ${TARGET}`);
let outputFileName = 'app';
outputFileName += TARGET === 'prod' ? '.min.js' : '.js';
const common = {
entry: ['babel-polyfill', 'react-hot-loader/patch', './index.jsx'],
output: {
publicPath: '/',
},
module: {
rules: [
{
test: /\.js[x]?$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
},
},
],
},
plugins: [
new webpack.ProvidePlugin({
jQuery: 'jquery',
$: 'jquery',
jquery: 'jquery',
'window.jQuery': 'jquery',
}),
new WebpackNotifierPlugin(),
],
resolve: {
modules: [
path.resolve('.'),
path.resolve('script'),
path.resolve('script', 'views'),
'node_modules',
],
extensions: ['.js', '.jsx', '.json'],
},
};
if (TARGET === 'dev' || !TARGET) {
module.exports = webpackMerge(common, {
devtool: 'eval-source-map',
output: {
filename: 'bundle.js',
sourceMapFilename: '[file].map',
},
module: {
rules: [
{
test: /\.scss$/,
loaders: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
config: {
path: 'postcss.config.js',
},
},
},
'sass-loader',
],
},
{
test: /\.less$/,
loaders: ['style-loader', 'css-loader', 'less-loader'],
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(eot|ttf|svg|gif|png|jpg|otf|woff|woff2)$/,
loader: 'url-loader',
},
],
},
devServer: {
contentBase: path.resolve(__dirname), // New
historyApiFallback: true,
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development'),
}),
new webpack.HotModuleReplacementPlugin(),
],
});
}
index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import createHistory from 'history/createBrowserHistory';
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { ConnectedRouter, routerReducer, routerMiddleware } from 'react-router-redux';
import { Provider } from 'react-redux';
import { AppContainer } from 'react-hot-loader';
import createSagaMiddleware from 'redux-saga';
import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly';
import 'bootstrap';
import 'styles/custom.scss';
import App from 'App';
import reducers from 'state';
import sagas from 'sagas';
const history = createHistory();
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
combineReducers({
...reducers,
router: routerReducer,
}),
composeWithDevTools(applyMiddleware(routerMiddleware(history), sagaMiddleware)),
);
if (module.hot) {
module.hot.accept('state', () => {
store.replaceReducer(require('state').default);
});
}
sagaMiddleware.run(sagas);
const render = (Component) => {
ReactDOM.render(
<AppContainer>
<Provider store={store}>
<ConnectedRouter history={history}>
<Component />
</ConnectedRouter>
</Provider>
</AppContainer>,
document.getElementById('app'),
);
};
render(App);
if (module.hot) {
module.hot.accept('App', () => { render(App); });
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Test</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
</head>
<body>
<div id="app">
</div>
<script type="text/javascript" src="/bundle.js"></script>
</body>
</html>
- 您可以使用
react-script
和react-app-rewire-hot-loader
你可能不会用
react-hot-loader/babel
{ test: /\.(js|jsx)$/, include: paths.appSrc, loader: require.resolve("babel-loader"), options: { // This is a feature of babel-loader for webpack (not Babel itself). // It enables caching results in ./node_modules/.cache/babel-loader/ // directory for faster rebuilds. cacheDirectory: true, plugins: ["react-hot-loader/babel"] }, }
您是否尝试过将根组件导出为热组件:
import { hot } from "react-hot-loader"
const App = () => <h3>Hello World</h3>
export default hot(module)(App)
您定义了 Hot Module Replacement 两次。您在 package.json
中定义了 --hot
参数,然后在 webpack.config.js
中定义了 new webpack.HotModuleReplacementPlugin()
。你应该只定义一次。