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

相关文章

Spring Boot集成/输出/日志级别控制/持久化开发实践

《SpringBoot集成/输出/日志级别控制/持久化开发实践》SpringBoot默认集成Logback,支持灵活日志级别配置(INFO/DEBUG等),输出包含时间戳、级别、类名等信息,并可通过... 目录一、日志概述1.1、Spring Boot日志简介1.2、日志框架与默认配置1.3、日志的核心作用

PyQt5 GUI 开发的基础知识

《PyQt5GUI开发的基础知识》Qt是一个跨平台的C++图形用户界面开发框架,支持GUI和非GUI程序开发,本文介绍了使用PyQt5进行界面开发的基础知识,包括创建简单窗口、常用控件、窗口属性设... 目录简介第一个PyQt程序最常用的三个功能模块控件QPushButton(按钮)控件QLable(纯文本

游戏闪退弹窗提示找不到storm.dll文件怎么办? Stormdll文件损坏修复技巧

《游戏闪退弹窗提示找不到storm.dll文件怎么办?Stormdll文件损坏修复技巧》DLL文件丢失或损坏会导致软件无法正常运行,例如我们在电脑上运行软件或游戏时会得到以下提示:storm.dll... 很多玩家在打开游戏时,突然弹出“找不到storm.dll文件”的提示框,随后游戏直接闪退,这通常是由于

基于Python开发一个图像水印批量添加工具

《基于Python开发一个图像水印批量添加工具》在当今数字化内容爆炸式增长的时代,图像版权保护已成为创作者和企业的核心需求,本方案将详细介绍一个基于PythonPIL库的工业级图像水印解决方案,有需要... 目录一、系统架构设计1.1 整体处理流程1.2 类结构设计(扩展版本)二、核心算法深入解析2.1 自

MySQL 多列 IN 查询之语法、性能与实战技巧(最新整理)

《MySQL多列IN查询之语法、性能与实战技巧(最新整理)》本文详解MySQL多列IN查询,对比传统OR写法,强调其简洁高效,适合批量匹配复合键,通过联合索引、分批次优化提升性能,兼容多种数据库... 目录一、基础语法:多列 IN 的两种写法1. 直接值列表2. 子查询二、对比传统 OR 的写法三、性能分析

Python使用vllm处理多模态数据的预处理技巧

《Python使用vllm处理多模态数据的预处理技巧》本文深入探讨了在Python环境下使用vLLM处理多模态数据的预处理技巧,我们将从基础概念出发,详细讲解文本、图像、音频等多模态数据的预处理方法,... 目录1. 背景介绍1.1 目的和范围1.2 预期读者1.3 文档结构概述1.4 术语表1.4.1 核

Java中的雪花算法Snowflake解析与实践技巧

《Java中的雪花算法Snowflake解析与实践技巧》本文解析了雪花算法的原理、Java实现及生产实践,涵盖ID结构、位运算技巧、时钟回拨处理、WorkerId分配等关键点,并探讨了百度UidGen... 目录一、雪花算法核心原理1.1 算法起源1.2 ID结构详解1.3 核心特性二、Java实现解析2.

深度解析Python装饰器常见用法与进阶技巧

《深度解析Python装饰器常见用法与进阶技巧》Python装饰器(Decorator)是提升代码可读性与复用性的强大工具,本文将深入解析Python装饰器的原理,常见用法,进阶技巧与最佳实践,希望可... 目录装饰器的基本原理函数装饰器的常见用法带参数的装饰器类装饰器与方法装饰器装饰器的嵌套与组合进阶技巧

SpringBoot开发中十大常见陷阱深度解析与避坑指南

《SpringBoot开发中十大常见陷阱深度解析与避坑指南》在SpringBoot的开发过程中,即使是经验丰富的开发者也难免会遇到各种棘手的问题,本文将针对SpringBoot开发中十大常见的“坑... 目录引言一、配置总出错?是不是同时用了.properties和.yml?二、换个位置配置就失效?搞清楚加

Go语言代码格式化的技巧分享

《Go语言代码格式化的技巧分享》在Go语言的开发过程中,代码格式化是一个看似细微却至关重要的环节,良好的代码格式化不仅能提升代码的可读性,还能促进团队协作,减少因代码风格差异引发的问题,Go在代码格式... 目录一、Go 语言代码格式化的重要性二、Go 语言代码格式化工具:gofmt 与 go fmt(一)