如何让 react-hot-loader 与 webpack 2 和 webpackDevMiddleware 一起工作?
How to get react-hot-loader working with webpack 2 and webpackDevMiddleware?
我正在使用 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/patch
、react-hot-loader/babel
和 react-hot-loader/webpack
来自 react-hot-loader@3
:
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'
],
},
但其中 none 似乎有效。我刚收到此错误消息:
[HMR] The following modules couldn't be hot updated: (Full reload needed)
This is usually because the modules which have changed (and their parents) do not know how to hot reload themselves. See http://webpack.github.io/docs/hot-module-replacement-with-webpack.html for more details.
logUpdates @ bundle.js:29964
applyCallback @ bundle.js:29932
(anonymous) @ bundle.js:29940
bundle.js:29972
[HMR] - ./client/components/CrawlForm.jsx
让它发挥作用的诀窍是什么?
N.B。 CSS 热加载工作得很好,所以我让那部分工作了。
我折腾了好几天终于破案了。这是我的有效代码:
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()}
添加到我的 <Router />
组件中。
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'));
});
}
祝你好运
转述自Dan Abramov and borrowing some code from ,完整说明为:
yarn add react-hot-loader@3
- 更新
webpack.config.js
:
- 将
react-hot-loader/patch
和 webpack-hot-middleware/client
添加到 entry
的顶部
- 将
react-hot-loader/babel
添加到您的 babel-loader
plugins
- 将
new HotModuleReplacementPlugin()
添加到您的 webpack 插件中
加上webpack-dev-middleware
和webpack-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}`);
});
将 <AppContainer>
和 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);
});
}
我在获取错误叠加层以呈现运行时错误以及从中恢复时遇到了一些麻烦。我注意到 webpack-dev-server
会在发生错误时进行完全重新加载。
这可以用以下代码片段模拟:
if (module.hot) module.hot.accept('./App', () => {
try {
render(App)
} catch (e) {
location.reload();
}
});
我的作品 fork of react-hot-boilerplate 可以在 Github 上找到。
我也在我的应用程序中添加了每个点,但它没有用
问题出在我的 webpack.config.js
中的 publicPath
我有
publicPath: '/client/dist'
然后我改成了
publicPath: '/'
现在可以使用了
我正在使用 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/patch
、react-hot-loader/babel
和 react-hot-loader/webpack
来自 react-hot-loader@3
:
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'
],
},
但其中 none 似乎有效。我刚收到此错误消息:
[HMR] The following modules couldn't be hot updated: (Full reload needed) This is usually because the modules which have changed (and their parents) do not know how to hot reload themselves. See http://webpack.github.io/docs/hot-module-replacement-with-webpack.html for more details. logUpdates @ bundle.js:29964 applyCallback @ bundle.js:29932 (anonymous) @ bundle.js:29940 bundle.js:29972
[HMR] - ./client/components/CrawlForm.jsx
让它发挥作用的诀窍是什么?
N.B。 CSS 热加载工作得很好,所以我让那部分工作了。
我折腾了好几天终于破案了。这是我的有效代码:
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()}
添加到我的 <Router />
组件中。
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'));
});
}
祝你好运
转述自Dan Abramov and borrowing some code from
yarn add react-hot-loader@3
- 更新
webpack.config.js
:- 将
react-hot-loader/patch
和webpack-hot-middleware/client
添加到entry
的顶部
- 将
react-hot-loader/babel
添加到您的babel-loader
plugins
- 将
new HotModuleReplacementPlugin()
添加到您的 webpack 插件中
- 将
加上
webpack-dev-middleware
和webpack-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}`); });
将
<AppContainer>
和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); }); }
我在获取错误叠加层以呈现运行时错误以及从中恢复时遇到了一些麻烦。我注意到 webpack-dev-server
会在发生错误时进行完全重新加载。
这可以用以下代码片段模拟:
if (module.hot) module.hot.accept('./App', () => {
try {
render(App)
} catch (e) {
location.reload();
}
});
我的作品 fork of react-hot-boilerplate 可以在 Github 上找到。
我也在我的应用程序中添加了每个点,但它没有用
问题出在我的 webpack.config.js
中的 publicPath
我有
publicPath: '/client/dist'
然后我改成了
publicPath: '/'
现在可以使用了