简体   繁体   English

用 SSR 反应 HMR

[英]React HMR with SSR

I am trying to setup SSR for React-application, when I am starting server at the first time in development environment all works good (server sending browser HTML and CSS), after changing source code of my application I getting an error:我正在尝试为 React 应用程序设置 SSR,当我第一次在development环境中启动服务器时一切正常(服务器发送浏览器 HTML 和 CSS),在更改我的应用程序的源代码后,出现错误:

在此处输入图片说明

This error was throwing because on server source code is outdated, but client has a new version and React notify me over this trouble.抛出这个错误是因为服务器上的源代码已经过时,但是客户端有一个新版本并且 React 通知我这个问题。 I think the solve of this trouble is mechanism called HMR (hot module replacement), but setting up this is hard for me.我认为解决这个问题的方法是一种称为 HMR(热模块更换)的机制,但是设置它对我来说很困难。

My server Webpack-config looks like this:我的服务器 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'],
  },
});

On each request server render a new version of UI在每个请求服务器上呈现一个新版本的 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);
});

For the hot reload I use webpackDevMiddleware and webpackHotMiddleware对于热重载,我使用webpackDevMiddlewarewebpackHotMiddleware

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 function intended for render App to NodeStream :用于将App渲染到NodeStream renderDocumentToStream函数:

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');
  });
}

When server is starting in stdout logged first call of console.log当服务器在stdout启动时记录了console.log第一次调用

在此处输入图片说明

second call of console.log not logged, even after App.jsx has been changed即使App.jsx已更改,第二次调用console.log未记录

在此处输入图片说明

What a im doing wrong?我做错了什么?

The warning is clearly indicating that the content is different at client from server. 警告清楚地表明客户端与服务器的内容不同。 This is not an error but a warning which means, there are going to be performance impact because React has to re-render at client and so the whole purpose of having SSR will be defeated. 这不是一个错误,而是一个警告,这意味着,会有性能影响,因为React必须在客户端重新渲染,因此SSR的整个目的将被打败。 Please check and make sure that client is also getting the same for hydration as the server. 请检查并确保客户端也获得与服务器相同的水合作用。 This should fix the issue. 这应该解决问题。

I do not think it is possible to solve because the server side will not refresh on change with HMR. 我不认为有可能解决,因为服务器端不会刷新HMR的更改。

Personally when doing React with SSR I setup nodemon to listen to file changes on the SSR output files and restart the application. 个人在使用SSR进行React时我设置nodemon来监听SSR输出文件上的文件更改并重新启动应用程序。 Unfortunately this isn't as fast as HMR and you lose current state, but I do not think there is an alternative if you will be using SSR (or ignore the warnings) 不幸的是,这并不像HMR那么快,你失去了当前的状态,但我不认为如果你将使用SSR(或忽略警告)有另一种选择

This is also the case when using Next.js: https://github.com/zeit/next.js/issues/791 使用Next.js时也是如此: https//github.com/zeit/next.js/issues/791

This seemed to fix the client and server mismatch issue during HMR updates for my react-ssr-kit : 这似乎解决了我的react-ssr-kit的 HMR更新期间的客户端和服务器不匹配问题:

 const renderMethod = module.hot ? ReactDOM.render : ReactDOM.hydrate;

My setup is slightly different, but it should still work for yours: 我的设置略有不同,但它仍然适用于您的:

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}`);
    }
  });
}

This warning could mean you have a component that needs to render differently on the client side, and you are not making sure your "first-pass" render is the same on the server and the client side.此警告可能意味着您有一个组件需要在客户端以不同方式呈现,并且您没有确保“第一遍”呈现在服务器端和客户端是相同的。

It is okay for them to differ, for instance, if you load a spinner when there is no data, and this data is not going to be there on the server side, then go ahead and guarantee the spinner always renders on the server and on the "first-pass" render on the browser (the render call before componentDidMount() is fired) are identical.它们可以不同,例如,如果您在没有数据时加载微调器,并且这些数据不会在服务器端存在,那么继续并确保微调器始终在服务器上呈现浏览器上的“第一遍”渲染(在 componentDidMount() 被触发之前的渲染调用)是相同的。

Then when your data is there trigger a state change to "2nd-pass" render.然后,当您的数据存在时,触发状态更改为“2nd-pass”渲染。

Also, one thing I warn against is using the module.hot ? ReactDOM.render : ReactDOM.hydrate另外,我警告的一件事是使用module.hot ? ReactDOM.render : ReactDOM.hydrate module.hot ? ReactDOM.render : ReactDOM.hydrate . module.hot ? ReactDOM.render : ReactDOM.hydrate . module.hot ? ReactDOM.render : ReactDOM.hydrate

I regretted using this method at work, because it masks hard to detect mismatches in development.我后悔在工作中使用这种方法,因为它掩盖了在开发中难以检测的不匹配。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM