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开发一个带EPUB转换功能的Markdown编辑器

《使用Python开发一个带EPUB转换功能的Markdown编辑器》Markdown因其简单易用和强大的格式支持,成为了写作者、开发者及内容创作者的首选格式,本文将通过Python开发一个Markd... 目录应用概览代码结构与核心组件1. 初始化与布局 (__init__)2. 工具栏 (setup_t

Spring Shell 命令行实现交互式Shell应用开发

《SpringShell命令行实现交互式Shell应用开发》本文主要介绍了SpringShell命令行实现交互式Shell应用开发,能够帮助开发者快速构建功能丰富的命令行应用程序,具有一定的参考价... 目录引言一、Spring Shell概述二、创建命令类三、命令参数处理四、命令分组与帮助系统五、自定义S

Python通过模块化开发优化代码的技巧分享

《Python通过模块化开发优化代码的技巧分享》模块化开发就是把代码拆成一个个“零件”,该封装封装,该拆分拆分,下面小编就来和大家简单聊聊python如何用模块化开发进行代码优化吧... 目录什么是模块化开发如何拆分代码改进版:拆分成模块让模块更强大:使用 __init__.py你一定会遇到的问题模www.

Spring Security基于数据库的ABAC属性权限模型实战开发教程

《SpringSecurity基于数据库的ABAC属性权限模型实战开发教程》:本文主要介绍SpringSecurity基于数据库的ABAC属性权限模型实战开发教程,本文给大家介绍的非常详细,对大... 目录1. 前言2. 权限决策依据RBACABAC综合对比3. 数据库表结构说明4. 实战开始5. MyBA

使用Python开发一个简单的本地图片服务器

《使用Python开发一个简单的本地图片服务器》本文介绍了如何结合wxPython构建的图形用户界面GUI和Python内建的Web服务器功能,在本地网络中搭建一个私人的,即开即用的网页相册,文中的示... 目录项目目标核心技术栈代码深度解析完整代码工作流程主要功能与优势潜在改进与思考运行结果总结你是否曾经

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

MyBatis 动态 SQL 优化之标签的实战与技巧(常见用法)

《MyBatis动态SQL优化之标签的实战与技巧(常见用法)》本文通过详细的示例和实际应用场景,介绍了如何有效利用这些标签来优化MyBatis配置,提升开发效率,确保SQL的高效执行和安全性,感... 目录动态SQL详解一、动态SQL的核心概念1.1 什么是动态SQL?1.2 动态SQL的优点1.3 动态S

Python基于wxPython和FFmpeg开发一个视频标签工具

《Python基于wxPython和FFmpeg开发一个视频标签工具》在当今数字媒体时代,视频内容的管理和标记变得越来越重要,无论是研究人员需要对实验视频进行时间点标记,还是个人用户希望对家庭视频进行... 目录引言1. 应用概述2. 技术栈分析2.1 核心库和模块2.2 wxpython作为GUI选择的优

利用Python开发Markdown表格结构转换为Excel工具

《利用Python开发Markdown表格结构转换为Excel工具》在数据管理和文档编写过程中,我们经常使用Markdown来记录表格数据,但它没有Excel使用方便,所以本文将使用Python编写一... 目录1.完整代码2. 项目概述3. 代码解析3.1 依赖库3.2 GUI 设计3.3 解析 Mark

电脑win32spl.dll文件丢失咋办? win32spl.dll丢失无法连接打印机修复技巧

《电脑win32spl.dll文件丢失咋办?win32spl.dll丢失无法连接打印机修复技巧》电脑突然提示win32spl.dll文件丢失,打印机死活连不上,今天就来给大家详细讲解一下这个问题的解... 不知道大家在使用电脑的时候是否遇到过关于win32spl.dll文件丢失的问题,win32spl.dl