Nextjs 生产应用程序上的页面刷新中断样式

Posted

技术标签:

【中文标题】Nextjs 生产应用程序上的页面刷新中断样式【英文标题】:Page refresh break styles on Nextjs production app 【发布时间】:2020-05-20 11:13:02 【问题描述】:

我有一个使用 Nextjs 构建的网站,它会在页面刷新时或当用户直接访问网站到特定路径而不是根路径时破坏样式。例如https://vinnieography.web.app/contacts(链接看起来还可以的话,尝试刷新看看)

该网站托管在 Firebase Functions 上并使用 Nextjs 和 Ant design components。

网站刷新前的屏幕截图

刷新后网站的屏幕截图(注意丢失的导航)

导航并没有完全丢失,但它变成了一个移动导航,其图标未显示,但当您在导航区域附近悬停时,您会看到一个带有导航链接的下拉菜单。

我的 next.config.js

const withCss = require('@zeit/next-css')

module.exports = withCss(
  webpack: (config,  isServer ) => 
    if (isServer) 
      const antStyles = /antd\/.*?\/style\/css.*?/
      const origExternals = [...config.externals]
      config.externals = [
        (context, request, callback) => 
          if (request.match(antStyles)) return callback()
          if (typeof origExternals[0] === 'function') 
            origExternals[0](context, request, callback)
           else 
            callback()
          
        ,
        ...(typeof origExternals[0] === 'function' ? [] : origExternals),
      ]

      config.module.rules.unshift(
        test: antStyles,
        use: 'null-loader',
      )
    

    // Fixes npm packages that depend on `fs` module
    config.node = 
      fs: 'empty'
    

    return config
  ,
  distDir: "../../dist/client"
)

Nextjs、React 和 Antd 的版本。

"antd": "^3.24.2",
"next": "^9.0.2",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"@zeit/next-css": "^1.0.1",

【问题讨论】:

主页也坏了(尝试刷新),这可能是您的问题吗? github.com/zeit/next.js/issues/4597 您遇到了涉及 styled-components 包的补水问题。简而言之,您的服务器和客户端 classNames 不匹配(请检查您的开发控制台,这是最先出现的警告之一)。使用此示例设置自定义_document.js 文件以正确配置styled-components:github.com/zeit/next.js/blob/master/examples/…。此外,您还有一些其他警告需要解决,但此配置至少应该解决样式问题。 @ArchNoob 很高兴这就是问题所在。简单的修复。对于其他警告,请阅读有关在 s-s-r 中使用 useLayoutEffect 的信息:gist.github.com/gaearon/e7d97cdf38a2907924ea12e4ebdf3c85 useLayoutEffect 适合在客户端进行预渲染、布局计算,但是,由于服务器没有window,生命周期和窗口函数将无法按预期工作.相反,您应该延迟执行,直到加载客户端。我建议延迟加载组件。换句话说,渲染块(参见官方 React 文档中的 conditional rendering)用户看到的内容,直到计算出窗口并且 UI 准备好完全渲染。 @MattCarlotta,我认为您应该将您的评论移至答案,因为它有助于解决问题 【参考方案1】:

为了可靠地执行服务器端渲染并毫无问题地获取客户端包,您需要使用我们的 babel 插件。它通过向每个样式化组件添加确定性 ID 来防止校验和不匹配。更多信息请参考工具documentation。


 "plugins": [
   [
     "babel-plugin-styled-components",
    
     "s-s-r": false
    
  ]
 ]

【讨论】:

【参考方案2】:

TLDR:您不能在 Next.js 中使用基于移动断点的条件渲染,因为服务器端渲染无法访问浏览器的尺寸。这解释了刷新时的视觉失真。使用服务器端渲染检测断点的唯一可靠方法是使用 CSS 媒体查询来隐藏/取消隐藏移动组件。

据此blog:

服务器无法识别窗口和文档。这意味着设备无法检测到强制性属性(例如客户端的视口尺寸)——因此它需要以某种方式推断它们,这意味着响应方式非常有限且不准确。

例如,假设我们有一个使用 matchMedia 的应用程序 (您可能知道,它是一个 Web API,它位于 thewindow) 根据视口有条件地渲染组件 方面。您希望服务器如何呈现标记 没有窗户,即使它被假设以某种方式填充, 尺寸呢?一旦最初它会如何回应 render 包含一个响应组件,有条件地受 断点?

简单地说——这可能会导致服务器渲染我们的应用程序 错误地,最终导致部分水合作用 不匹配(即潜在的错误?)。

【讨论】:

什么是基于移动断点的条件渲染...? @ArchNoob 如果你的代码中有 isMobile? <MobileComponent/> 这样的东西。即:使用javascript检测屏幕宽度并基于此渲染组件。 当然,我在这个项目中使用了一些。【参考方案3】:

使用动态导入 s-s-r:false 来导入组件会解决这个问题

import dynamic from 'next/dynamic'

const DynamicComponentWithNos-s-r = dynamic(
  () => import('../components/hello3'),
   s-s-r: false 
)

【讨论】:

【参考方案4】:

我在使用带有 MaterialUI 的 NextJS 9.4 时遇到了这个问题。

每当样式损坏时,我都会在控制台中收到此警告

警告:道具className 不匹配。服务器:“MuiBox-root MuiBox-root-5" 客户端:"MuiBox-root MuiBox-root-1" 跨度(由 Styled(MuiBox) 创建)

我就是这样解决的。

我们需要自定义 document.js 和自定义 app.js,如 MaterialUI NextJS example 中所述

从这里复制_document.js_app.js https://github.com/mui-org/material-ui/tree/master/examples/nextjs/pages

pages文件夹

从https://github.com/mui-org/material-ui/tree/master/examples/nextjs/src复制theme.js并将其放置在某处并更新_document.js_app.js中的导入链接

样式现在应该可以使用了。

【讨论】:

【参考方案5】:

如果按照 Material-UI NextJS 示例中的建议修改应用程序没有帮助,您可以延迟加载您的组件。这样,您将强制它仅在加载客户端后创建样式。

为组件禁用 s-s-r 的指南:https://nextjs.org/docs/advanced-features/dynamic-import#with-no-s-s-r

import dynamic from 'next/dynamic'

export const ComponentWithNos-s-r = dynamic(() => import('./Component'), 
  s-s-r: false,
)

但是,请记住,该组件将失去 s-s-r 的所有特权。

【讨论】:

这非常有效。我希望有一种方法可以使用 s-s-r 为客户端上的反应组件“补充水分”

以上是关于Nextjs 生产应用程序上的页面刷新中断样式的主要内容,如果未能解决你的问题,请参考以下文章

Nextjs 页面刷新总是把我带到 index.js

CSS样式有时仅在jsf页面上的页面刷新后应用?

next js 中特定页面的全局样式

重新加载/刷新操作时出现 Nextjs 404 错误

NextJS React Tailwind CSS Navbar 活动样式

在下一个 js 中使用 webpack5 刷新不需要的页面