React/nextJS: 如何调试不同节点的SSR react应用?

React/nextJS: How to debug different nodes of SSR react application?

我正在 运行ning nextJS 应用程序,它是 运行ning SSR。

但是当我收到错误时:

Warning: Did not expect server HTML to contain a <div> in <div>.

因此服务器端和客户端节点之间似乎存在差异。我怎样才能找到这些差异?

这是示例应用程序的存储库:

https://github.com/jaqua/nextjs-app

只是 运行 npm installnpm run dev

我会首先查看进入浏览器的 html(chrome devtools 中的网络选项卡),然后 React 可能正在渲染客户端,因此您可以看到当前的 DOM 在客户端渲染和比较之后(转到 chrome devtools 中的元素选项卡 -> 右键单击​​ html 元素和 select "copy> copy outterHTML")

如果还是不行,你可以试试在浏览器里面react本身加断点: 函数 canHydrateInstance @ ReactDOMHostConfig.js

https://github.com/facebook/react/blob/c954efa70f44a44be9c33c60c57f87bea6f40a10/packages/react-dom/src/client/ReactDOMHostConfig.js

可能与同类问题相关的链接:

https://github.com/zeit/next.js/issues/5367

由于手动比较两个 html 可能相当麻烦,具体取决于页面的大小,建议首先评估可能出现的错误,而不是暴力破解。根据我在 99% 的情况下的经验,当您执行以下任一操作时,就会发生 SSR 不匹配:

  • 包含并呈现了一个组件,该组件在客户端和服务器上的行为方式不同(例如,它们使用全局变量来确定代码的位置 运行 并基于此有条件地呈现元素) .例如,有一个剪贴板模块只能在客户端上工作,因为它会使用 window.
  • 变量
  • 呈现从仅出现在服务器或客户端上的异步源获取的数据。您需要在初始渲染期间为两者提供相同的数据。

如果这之后什么都想不起来,就需要进行淘汰了。如果每个页面都出现错误,则很可能是服务器配置错误的结果。例如,你在做自己的 renderToString 吗?仔细检查你没有在其中添加额外的嵌套 div,字符串应该就在你挂载 React 的元素内。

如果不是这种情况,请尝试一个一个地提取您正在渲染的组件,您应该能够很快缩小导致问题的范围。

另外请记住,每次进行更改时都需要重新启动服务器(除非您有 nodemon 或类似的配置在您修改源代码时重新加载服务器端代码)才能应用它!


作为最后的手段,您可以在服务器响应和客户端首次呈现之间做出您自己的 diff

1) 从您的站点打开您的控制台,然后粘贴以下内容:

console.log(document.documentElement.innerHTML)

2) 单击 Copy 按钮,并将其粘贴到 client.html 文件中

3) 现在 运行 在您的终端中:

curl YOUR_URL > server.html 

4) 服务器可能会 return 你 html 的缩小版本,所以你需要缩进它以使其与你的客户端匹配 html,为此目的使用一些东西like this

5) 完成此操作后,您现在可以 运行 终端中的实际差异:

diff server.html client.html

这将列出文件中彼此不同的每个部分。 您可以忽略与 Javascript 相关的差异,因为缩进很可能无论如何都会很糟糕,但请专注于 html 的差异,您可以在其中发现差异并推断出问题所在。


在您的情况下,您的翻译系统很可能是问题的根本原因。我建议遵循更多标准做法,而不是 next-i18next,后者看起来很新,而且更可能出现问题。别人貌似也有an issue with SSR, and to be honest stuff like this好吓人

我知道设置起来可能有点麻烦,但这是我自己的 i18n 配置,如果您指定一个全局变量来确定您所在的环境,服务器或客户端可能需要它(这里 __BROWSER__).

import i18n from 'i18next'
import LanguageDetector from 'i18next-browser-languagedetector'
import { reactI18nextModule } from 'react-i18next'

i18n
  .use(require(__BROWSER__ ? 'i18next-xhr-backend' : 'i18next-node-fs-backend'))
  .use(LanguageDetector)
  .use(reactI18nextModule)
  .init({
    fallbackLng: 'en',
    ns: ['translations'],
    defaultNS: 'translations',

    interpolation: {
      escapeValue: false,
    },

    react: {
      wait: true,
    },

    backend: {
      loadPath: __BROWSER__
        ? '/locales/{{lng}}/{{ns}}.json'
        : require('path').join(__dirname, '/locales/{{lng}}/{{ns}}.json'),
    },
  })

export default i18n

您只需要使用中间件,从您的服务器提供语言环境,以便客户端可以从 xhr 加载它们并让 I18nextProvider 需要 i18n 实例。完整的 SSR 文档是 here.