Remix 开发小技巧(四)

2023-10-15 10:04
文章标签 技巧 开发 remix

本文主要是介绍Remix 开发小技巧(四),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 解决水合错误
    • 水合错误是什么样的?
    • 🔥 提示:对 HTML 进行差异以查找确切原因
    • 浏览器扩展程序或广告拦截器
    • 无效的 HTML
    • 第三方脚本或非 React 包
    • JS库中的CSS
    • 字符编码
    • 时区不匹配
    • 非幂等函数,如 UUID
    • 基于客户端数据呈现
  • 逐步增强客户端渲染,以避免 Remix 中的 SSR 水合问题

解决水合错误

人们通常将水合错误描述为服务器生成的HTML与浏览器生成的HTML不匹配,但这并不是故事的全部。

为了解释这一点,让我们看看 React 的服务器端渲染是如何工作的。大多数实现都遵循相同的步骤。

  1. 服务器呈现页面并向客户端提供 HTML。
  2. 浏览器加载页面,React 开始运行。
  3. React 重新渲染页面并生成自己的 HTML。
  4. 然后,React 将它生成的 HTML 与浏览器显示的 HTML 进行比较。
  5. 如果 HTML 匹配,那就太好了!否则,React 将抛出一个水合错误。

由于 React 正在将它生成的 HTML 与浏览器显示的 HTML 进行比较,因此在服务器发送它和 React 开始运行的时间之间更改 HTML 的任何内容都可能导致水合错误。

水合错误是什么样的?

水合错误的主要症状是 React 会放弃其服务器渲染的内容并执行完整的客户端渲染。这可能会导致页面闪烁并完全重新获取数据。

在开发中,您将看到稍微有用的错误消息。

  • 冻结失败,因为初始 UI 与服务器上呈现的内容不匹配。
  • 警告:应服务器 HTML 包含 中的 匹配 项。

🔥 提示:对 HTML 进行差异以查找确切原因

在我们继续讨论水合错误的常见原因之前,一个超级方便的技巧是将服务器发送的 HTML 与 React 生成的 HTML 进行差异。

有三个地方可以寻找这个。

  • 浏览器开发工具中的“网络”选项卡将显示服务器发送的 HTML。
  • 如果删除 root.tsx 中的 , Remix 将不会水合,您可以使用 View Source 查看在水合之前存在的 HTML。
  • 如果比较这三个 HTML 源,通常可以找到水化错误的确切原因。

有许多在线HTML比较工具,只需谷歌一个并从三个来源中的两个粘贴HTML即可。

浏览器扩展程序或广告拦截器

浏览器扩展程序通常有权修改任何网站(包括您的应用)的实时页面内容。如果扩展在 React 有机会比较它之前修改了 HTML,这可能会导致水化错误。

在隐身/无痕浏览窗口中试用您的应用,并禁用所有扩展程序。如果错误消失,您就知道扩展程序导致了问题,并且您可能对此无能为力。

还有一些桌面级广告拦截器和安全软件可能会导致类似的问题。如果您正在运行其中之一,请尝试禁用它或在另一台设备上进行测试。您可以在工具中为您的网站添加例外。

无效的 HTML

浏览器会进行大量纠错,以确保即使HTML格式不正确,页面也能合理呈现。

例如,如果您非法嵌套在

,浏览器将移动 的

外部以使 HTML 有效。

HTML 中禁止嵌套表单,因此,如果表单中有表单,浏览器会将嵌套表单移动到父表单之外,成为其同级表单。

当 React 补水时,它会将它生成的 HTML 与浏览器显示的 HTML 进行比较。如果浏览器移动元素,React 将抛出水化错误。

这里的解决方案是编写有效的 HTML。

第三方脚本或非 React 包

第三方脚本也可能修改页面上的 HTML。这在HotJar或Google Tag Manager(GTM)等分析脚本中尤其常见。如果您使用的是第三方脚本,请尝试将其删除,看看错误是否消失。

如果这是问题所在,您可以在 React 完成补水后运行脚本。例如,可以使用 在 useEffect 第一次渲染后运行脚本。

JS库中的CSS

CSS 中的 CSS 是一种设置 React 应用程序样式的方法,它将样式定义为渲染的副作用。这意味着当服务器呈现页面时,样式不可用,并且服务器发送到客户端的 HTML 将与 React 生成的 HTML 不匹配。

Chakra、Emotion 和 Material UI 是 JS 库中流行的 CSS,存在此问题。

字符编码

字符编码问题通常会显示明显的错误消息,因此易于诊断。

[Error] Warning: Text content did not match.Server: "â€"Client: "’"

当服务器和客户端的字符编码不匹配时,会发生这种情况。最常见的原因是服务器正在发送 UTF-8 编码的文本,但客户端将其解释为 ISO-8859-1。

要解决此问题,请将以下元标记添加到 HTML 中。

<metahttp-equiv="Content-Type"content="text/html;charset=utf-8"
/>

时区不匹配

可能最臭名昭著的罪魁祸首是 Date 对象。约会很复杂,你越少处理它们,你的生活就会越快乐。

当javascript尝试创建日期时,它将使用运行它的机器的时区。这意味着,如果您在服务器上创建一个日期并将其发送到客户端,客户端将在其自己的时区中创建一个日期,该时区将与服务器的时区不同。

如果您尝试呈现日期,则当服务器日期与客户端日期不同时,您每天都会在接近午夜时遇到这些补水问题。

  • 最简单的解决方案是在服务器上将日期格式化为字符串并将其发送到客户端。
  • 客户端可以将时区/区域设置信息发送到服务器,服务器可以使用它来创建一个假日期,该日期将在冻结期间与客户端的时区匹配。
  • 基于客户端数据呈现部分中的更多解决方案: https://www.jacobparis.com/content/remix-hydration-errors#rendering-based-on-client-data

非幂等函数,如 UUID

如果您使用的是非幂等函数(例如 uuid 生成唯一 ID),则可能会收到冻结错误。这是因为服务器和客户端将生成不同的 ID。

您可以在服务器上生成 ID 并发送到客户端,也可以将种子传递给函数,以便服务器和客户端生成相同的 ID。

基于客户端数据呈现

某些数据仅存在于客户端上,在服务器首次呈现页面时将不可用。

例如,您可能有一个从本地存储中获取默认值的输入。如果在服务器上将输入呈现为空,然后在客户端上填充它,则会出现冻结错误和难看的空输入闪烁。

服务器无法知道客户端想要渲染的内容,因此您的解决方案是找到一种方法来隐藏服务器上和初始水合渲染期间的元素。

来自Remix Utils的useHydrated钩子将为您提供一个 isHydrated 布尔值,您可以使用该布尔值有条件地渲染元素。

This is the easiest solution but has its own caveats
这是最简单的解决方案,但有其自身的注意事项

  • 它仍然会产生空内容的闪光,尽管您可以使用淡入淡出效果来使其不那么刺耳。
  • 如果 javascript 加载失败,这些元素根本不会出现,从而打破了渐进式增强的故事。

我的首选解决方案是使用 ProgressiveClientOnly 组件,该组件将使用 CSS 隐藏服务器呈现的内容,并在 Javascript 可用时将其交换为客户端内容,否则它将显示服务器内容。

逐步增强客户端渲染,以避免 Remix 中的 SSR 水合问题

对于服务器端渲染的所有好处,也有一些事情变得更加困难,例如在客户端和服务器中显示相同的日期。

每当服务器可以渲染的内容与客户端可以呈现的内容不匹配时,您都会遇到问题。

处理不当的网站最终会出现无样式内容(FOUC)或不正确内容的闪烁(FOIC)。您可能会遇到补水错误,并导致您的网站完全依赖服务器端渲染。

有些人试图通过仅在客户端上呈现某些元素来解决此问题。这是避免不匹配的可靠方法,但这也意味着没有javascript的用户将永远不会看到该内容。

没有javascript的用户将获得服务器上呈现的任何版本的页面,因此,如果要逐步增强,服务器必须呈现内容。

也就是说,javascript将适用于大多数网站的大多数用户,因此我们想要针对这种情况进行优化。

我们希望确保该网站在没有Javascript的情况下运行,但是如果这意味着该网站对大多数用户来说效果很好,那么我可以让这种体验有点笨拙。

由于我们必须在服务器上呈现内容,但我们不想立即向用户显示它,因此我们需要以某种方式隐藏它。Javascript解决方案在这里不起作用。即使你告诉 React 立即隐藏它,在 React 接管之前,服务器渲染页面仍然会有闪光。

我们剩下的是一个CSS解决方案。

我构建了一个自定义包装器组件,该组件隐藏其内容,直到页面加载。如果禁用JS,它将通过CSS动画显示它们。如果没有,则可以通过传入类名来自定义行为。

import { useHydrated } from "remix-utils"
export function ProgressiveClientOnly({children,className = "",
}: {children: React.ReactNode | (() => React.ReactNode)className: string
}) {const isHydrated = useHydrated()return (<divclassName={// Create this class in your tailwind configisHydrated ? className : "animate-appear"}>{typeof children === "function"? children(): children}</div>)
}

该 animate-appear 类是一个自定义 CSS 动画,它使元素在延迟后突然出现。

module.exports = {theme: {extend: {animation: {appear: "appear 300ms",},keyframes: {appear: {"0%, 99%": {height: "0",width: "0",opacity: "0",},"100%": {height: "auto",width: "auto",opacity: "1",},},},},},
}

这篇关于Remix 开发小技巧(四)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/216973

相关文章

基于Python开发电脑定时关机工具

《基于Python开发电脑定时关机工具》这篇文章主要为大家详细介绍了如何基于Python开发一个电脑定时关机工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 简介2. 运行效果3. 相关源码1. 简介这个程序就像一个“忠实的管家”,帮你按时关掉电脑,而且全程不需要你多做

怎么关闭Ubuntu无人值守升级? Ubuntu禁止自动更新的技巧

《怎么关闭Ubuntu无人值守升级?Ubuntu禁止自动更新的技巧》UbuntuLinux系统禁止自动更新的时候,提示“无人值守升级在关机期间,请不要关闭计算机进程”,该怎么解决这个问题?详细请看... 本教程教你如何处理无人值守的升级,即 Ubuntu linux 的自动系统更新。来源:https://

Java中的Opencv简介与开发环境部署方法

《Java中的Opencv简介与开发环境部署方法》OpenCV是一个开源的计算机视觉和图像处理库,提供了丰富的图像处理算法和工具,它支持多种图像处理和计算机视觉算法,可以用于物体识别与跟踪、图像分割与... 目录1.Opencv简介Opencv的应用2.Java使用OpenCV进行图像操作opencv安装j

将Python应用部署到生产环境的小技巧分享

《将Python应用部署到生产环境的小技巧分享》文章主要讲述了在将Python应用程序部署到生产环境之前,需要进行的准备工作和最佳实践,包括心态调整、代码审查、测试覆盖率提升、配置文件优化、日志记录完... 目录部署前夜:从开发到生产的心理准备与检查清单环境搭建:打造稳固的应用运行平台自动化流水线:让部署像

基于Qt开发一个简单的OFD阅读器

《基于Qt开发一个简单的OFD阅读器》这篇文章主要为大家详细介绍了如何使用Qt框架开发一个功能强大且性能优异的OFD阅读器,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 目录摘要引言一、OFD文件格式解析二、文档结构解析三、页面渲染四、用户交互五、性能优化六、示例代码七、未来发展方向八、结论摘要

Java 枚举的常用技巧汇总

《Java枚举的常用技巧汇总》在Java中,枚举类型是一种特殊的数据类型,允许定义一组固定的常量,默认情况下,toString方法返回枚举常量的名称,本文提供了一个完整的代码示例,展示了如何在Jav... 目录一、枚举的基本概念1. 什么是枚举?2. 基本枚举示例3. 枚举的优势二、枚举的高级用法1. 枚举

不删数据还能合并磁盘? 让电脑C盘D盘合并并保留数据的技巧

《不删数据还能合并磁盘?让电脑C盘D盘合并并保留数据的技巧》在Windows操作系统中,合并C盘和D盘是一个相对复杂的任务,尤其是当你不希望删除其中的数据时,幸运的是,有几种方法可以实现这一目标且在... 在电脑生产时,制造商常为C盘分配较小的磁盘空间,以确保软件在运行过程中不会出现磁盘空间不足的问题。但在

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo

Python中列表的高级索引技巧分享

《Python中列表的高级索引技巧分享》列表是Python中最常用的数据结构之一,它允许你存储多个元素,并且可以通过索引来访问这些元素,本文将带你深入了解Python列表的高级索引技巧,希望对... 目录1.基本索引2.切片3.负数索引切片4.步长5.多维列表6.列表解析7.切片赋值8.删除元素9.反转列表

C#图表开发之Chart详解

《C#图表开发之Chart详解》C#中的Chart控件用于开发图表功能,具有Series和ChartArea两个重要属性,Series属性是SeriesCollection类型,包含多个Series对... 目录OverviChina编程ewSeries类总结OverviewC#中,开发图表功能的控件是Char