React热模块替换与服务器端渲染

6

我正在尝试为React应用程序设置SSR,当我在开发环境中首次启动服务器时,一切正常(服务器发送浏览器HTML和CSS),在更改应用程序源代码后,我遇到了一个错误:

enter image description here

这个错误是由于服务器端的源代码过时,但客户端有新版本并且React通过通知我解决了这个问题。我认为解决这个问题的机制叫做HMR(热模块替换),但对我来说设置它很困难。

我的服务器Webpack配置如下:

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

我使用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 函数用于将 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 的第一次调用。

enter image description here

即使更改了 App.jsxconsole.log 的第二次调用也不会被记录。

enter image description here

我做错了什么?


1
那不是问题,你能否先发布一下你有“Home”的代码? - evgeni fotia
这可能是因为在服务器端呈现的 HTML 与客户端要呈现的 HTML 不匹配。 - Sagi Rika
3个回答

1
警告明确指出客户端和服务器端的内容不同。这不是错误,而是一个警告,意味着会有性能影响,因为React必须在客户端重新渲染,所以具有SSR的整个目的将被打败。 请检查并确保客户端得到与服务器端相同的水合物。这应该解决问题。

0

我认为这是不可能解决的,因为服务器端在使用HMR时不会刷新。

个人在使用React和SSR时,我设置了nodemon监听SSR输出文件的更改并重新启动应用程序。不幸的是,这不如HMR快速,并且您会失去当前状态,但如果您将使用SSR(或忽略警告),我认为没有其他选择。

当使用Next.js时也是如此:https://github.com/zeit/next.js/issues/791


0

这个警告可能意味着您有一个组件需要在客户端上以不同的方式进行渲染,而且您没有确保“第一次”渲染在服务器和客户端上是相同的。

它们可以不同,例如,如果没有数据时加载旋转器,并且此数据在服务器端不会存在,则可以保证旋转器始终在服务器和浏览器的“第一次”渲染(在 componentDidMount() 被激活之前的渲染调用)中都相同。

然后当您的数据到达时,触发状态更改以进行“第二次”渲染。

此外,我警告您不要使用module.hot ? ReactDOM.render : ReactDOM.hydrate这种方法。

我曾经在工作中后悔使用这种方法,因为它掩盖了开发中难以检测到的不匹配问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接