Реагировать HMR с SSR - PullRequest
       1

Реагировать HMR с SSR

4 голосов
/ 03 апреля 2019

Я пытаюсь настроить SSR для React-приложения, когда я запускаю сервер в первый раз в среде development, все работает хорошо (сервер отправляет браузер HTML и CSS), после изменения исходного кода моего приложения я получаю ошибка:

enter image description here

Эта ошибка была вызвана тем, что на сервере исходный код устарел, но у клиента есть новая версия, и 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'],
  },
});

На каждом сервере запросов отображается новая версия пользовательского интерфейса

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

enter image description here

второй вызов console.log не регистрируется, даже после изменения App.jsx

enter image description here

Что я делаю не так?

Ответы [ 3 ]

1 голос
/ 07 апреля 2019

Я не думаю, что это можно решить, потому что сторона сервера не будет обновляться при изменении с помощью HMR.

Лично при выполнении React with SSR я настраиваю nodemon для прослушивания изменений файла на выходе SSRфайлы и перезапустите приложение.К сожалению, это не так быстро, как HMR, и вы теряете текущее состояние, но я не думаю, что есть альтернатива, если вы будете использовать SSR (или игнорировать предупреждения)

Это также имеет место при использовании Next.js: https://github.com/zeit/next.js/issues/791

1 голос
/ 07 апреля 2019

Это, похоже, исправило проблему несоответствия клиента и сервера во время обновлений HMR для моего act-ssr-kit :

 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}`);
    }
  });
}
1 голос
/ 07 апреля 2019

Предупреждение ясно указывает на то, что содержимое на клиенте отличается от сервера.Это не ошибка, а предупреждение, которое означает, что это повлияет на производительность, потому что React должен повторно выполнить рендеринг на клиенте, и поэтому цель использования SSR будет побеждена.Пожалуйста, проверьте и убедитесь, что клиент получает то же самое для гидратации, что и сервер.Это должно решить проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...