我花了几天时间最终解决了这个问题。以下是我可行的代码:
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,
stats: {
colors: true,
},
}));
app.use(webpackHotMiddleware(compiler));
app.use(express.static('build/public'));
const { commander: nameless, apiPrefix } = setupNameless(app);
app.use((req, res, next) => {
nameless.exec('jobs', actions.GET_JOBS).then((jobs) => {
const store = makeStore(Immutable.fromJS({
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) {
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
。另一个重要的方面是在我的<Router />
组件中添加key={Math.random()}
。
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'));
});
}
祝你好运
<AppContainer>
和if(module.hot)
部分对我有用。谢谢! - mpen