我正在尝试为React-application设置SSR,当我在development
环境中首次启动服务器时,在更改了我的应用程序的源代码后,它们都工作正常(服务器发送浏览器HTML和CSS)出现错误:
引发此错误是因为服务器上的源代码已过时,但是客户端具有新版本,React通知我此问题。我认为解决此问题的方法是称为HMR(热模块替换)的机制,但是对我来说很难设置。
我的服务器Webpack-config看起来像这样:
const serverConfig = merge(commonConfig, {
name: 'server',
target: 'node',
externals: [
nodeExternals({
whitelist: ['webpack/hot/poll?1000'],
}),
],
entry: ['webpack/hot/poll?1000', appServerEntry],
output: {
path: path.resolve(appDist, 'server'),
filename: 'index.js',
},
plugins: [new webpack.HotModuleReplacementPlugin()],
resolve: {
extensions: ['.js', '.jsx', '.json'],
},
});
在每个请求服务器上呈现新版本的UI
app.get('*', (request, response) => {
const staticAssets = getStaticAssets();
const routerContext = {};
const renderStream = renderDocumentToStream({
staticAssets,
request,
routerContext,
});
const cacheStream = createCacheStream(request.path);
response.writeHead(200, { 'Content-Type': 'text/html' });
response.write('<!DOCTYPE html>');
cacheStream.pipe(response);
renderStream.pipe(cacheStream);
});
对于热重装,我使用webpackDevMiddleware
和webpackHotMiddleware
const webpackInstance = webpack(webpackConfig);
const clientCompiler = webpackInstance.compilers.find(cmpl => cmpl.name === 'client');
app.use(
webpackDevMiddleware(clientCompiler, {
hot: true,
stats: 'errors-only',
}),
);
app.use(webpackHotMiddleware(clientCompiler));
renderDocumentToStream
函数,旨在将App
渲染为NodeStream
:
import App from './App';
renderDocumentToStream: ({ request, staticAssets, routerContext }) => {
const rootMarkup = renderToString(
<StaticRouter location={request.url} context={routerContext}>
<App />
</StaticRouter>
);
return renderToNodeStream(
<Document
rootMarkup={rootMarkup}
staticAssets={staticAssets}
/>,
);
},
if (module.hot) {
console.log('HERE-0');
module.hot.accept('./App', () => {
console.log('HERE-1');
});
}
服务器在stdout
中启动时,记录了console.log
的首次呼叫
console.log
,也未记录 App.jsx
的第二次呼叫
我在做什么错了?
答案 0 :(得分:1)
警告明确表明客户端与服务器上的内容不同。这不是一个错误,而是一个警告,这意味着将对性能产生影响,因为React必须在客户端重新渲染,因此实现SSR的整个目的将被挫败。 请检查并确保客户端也获得与服务器相同的水合作用。这应该可以解决问题。
答案 1 :(得分:1)
我认为不可能解决,因为服务器端不会随着HMR的更改而刷新。
就个人而言,在使用SSR执行React时,我设置了nodemon
来监听SSR输出文件上的文件更改并重新启动应用程序。不幸的是,这不如HMR快,并且您会丢失当前状态,但是我认为如果您要使用SSR(或忽略警告),则没有其他选择
使用Next.js时也是如此:https://github.com/zeit/next.js/issues/791
答案 2 :(得分:1)
这似乎解决了react-ssr-kit的HMR更新期间客户端和服务器不匹配的问题:
const renderMethod = module.hot ? ReactDOM.render : ReactDOM.hydrate;
我的设置略有不同,但仍适用于您的设置:
import React from "react";
import ReactDOM from "react-dom";
import { createBrowserHistory } from "history";
import App from "root";
import routes from "routes";
import configureStore from "store/configureStore";
const history = createBrowserHistory(); // create browserhistory
const initialState = window.__INITIAL_PROPS__; // grabs redux state from server on load
const store = configureStore(history, initialState); // sets up redux store with history and initial state
const renderApp = props => {
const renderMethod = module.hot ? ReactDOM.render : ReactDOM.hydrate; // if module is hot, use ReactDOM's render, else hydrate
renderMethod(<App {...props} />, document.getElementById("root"));
};
renderApp({ routes, history, store }); // initial App render (ReactDOM.hydrate)
// enable webpack hot module replacement
if (module.hot) {
module.hot.accept("./routes", () => {
try {
const nextRoutes = require("./routes").default;
renderApp({ routes: nextRoutes, history, store }); // hot-module updates (ReactDOM.render)
} catch (err) {
console.error(`Routes hot reloading error: ${err}`);
}
});
}
答案 3 :(得分:0)
此警告可能意味着您有一个组件需要在客户端进行不同的呈现,并且您无法确保服务器和客户端上的“初次通过”呈现都相同。
它们可以有所不同,例如,如果在没有数据的情况下加载微调器,并且该数据不会在服务器端存在,那么请继续确保微调器始终在服务器与浏览器上的“首过”渲染(激发componentDidMount()之前的渲染调用)相同。
然后,当数据存在时,触发状态更改为“第二遍”渲染。
此外,我要警告的一件事是使用module.hot ? ReactDOM.render : ReactDOM.hydrate
。
我后悔在工作中使用此方法,因为它掩盖了难以检测到开发中的不匹配的情况。