CSS 文件
在 Remix 中管理 CSS 文件主要有两种方式:
本指南介绍了每种方法的优缺点,并根据您的项目的具体需求提供了一些建议。
CSS 打包
CSS 打包是 React 社区中管理 CSS 文件的最常用方法。在此模型中,样式被视为模块副作用,并由打包器自行打包到一个或多个 CSS 文件中。它使用起来更简单,需要的样板更少,并为打包器提供了更多优化输出的能力。
例如,假设您有一个基本的按钮
组件,其中附加了一些样式:
要使用此组件,您只需导入它并在路由文件中使用它:
使用此组件时,您无需担心管理单个 CSS 文件。CSS 被视为组件的私有实现细节。这是许多组件库和设计系统中的常见模式,并且扩展性相当好。
某些 CSS 解决方案需要 CSS 捆绑
一些管理 CSS 文件的方法需要使用捆绑的 CSS。
例如,CSS 模块 是基于 CSS 已捆绑这一假设而构建的。即使您明确将 CSS 文件的类名导入为 JavaScript 对象,样式本身仍会被视为副作用,并自动捆绑到输出中。您无法访问 CSS 文件的底层 URL。
需要 CSS 捆绑的另一个常见用例是当您使用第三方组件库时,该库会将 CSS 文件作为副作用导入并依赖捆绑器为您处理它们,例如 React Spectrum。
CSS 顺序在开发和生产过程中可能有所不同
当与 Vite 的按需编译方法结合时,CSS 捆绑会带来明显的弊端。
使用前面介绍的 Button.css
示例,该 CSS 文件将在开发过程中转换为以下 JavaScript 代码:
值得强调的是,这种转变仅发生在开发过程中。生产版本不会像这样,因为会生成静态 CSS 文件。
Vite 这样做的目的是,CSS 可以在导入时延迟编译,然后在开发过程中进行热重载。一旦导入此文件,CSS 文件的内容就会作为副作用注入到页面中。
这种方法的缺点是这些样式与路由生命周期无关。这意味着在离开路由时样式不会被卸载,从而导致在应用中导航时文档中会累积旧样式。这可能会导致 CSS 规则顺序在开发和生产之间有所不同。
为了缓解这种情况,以能够抵御文件顺序变化的方式编写 CSS 会很有帮助。例如,您可以使用 CSS 模块 来确保 CSS 文件的范围仅限于导入它们的文件。您还应该尝试限制针对单个元素的 CSS 文件数量,因为这些文件的顺序无法保证。
捆绑的 CSS 可能会在开发过程中消失
Vite 在开发过程中处理 CSS 捆绑方法的另一个值得注意的缺点是,React 可能会无意中删除文档中的样式。
当使用 React 渲染整个文档(如 Remix 所做的那样)时,您可能会在将元素动态注入 head
元素时遇到问题。如果重新安装文档,现有的 head
元素将被删除并替换为一个全新的元素,从而删除 Vite 在开发过程中注入的所有 style
元素。
在 Remix 中,此问题可能由于 Hydration 错误而发生,因为它会导致 React 从头开始重新渲染整个页面。Hydration 错误可能是由您的应用代码引起的,但也可能是由操纵文档的浏览器扩展程序引起的。
这是一个已知的 React 问题,已在其 canary 发布渠道 中修复。如果您了解所涉及的风险,您可以将您的应用固定到特定的 React 版本,然后使用 package overrides 确保这是整个项目中使用的唯一 React 版本。例如:
再次强调,值得强调的是,Vite 注入的样式问题仅发生在开发阶段。生产版本不会出现此问题,因为会生成静态 CSS 文件。
CSS URL 导入
管理 CSS 文件的另一种主要方式是使用 Vite 的显式 URL 导入。
Vite 允许您将 ?url
附加到 CSS 文件导入中以获取文件的 URL(例如 import href from "./styles.css?url"
)。然后可以通过路由模块中的 links export 将此 URL 传递给 Remix。这会将 CSS 文件绑定到 Remix 的路由生命周期中,确保在应用程序中导航时从文档中注入和删除样式。
例如,使用之前相同的按钮
组件示例,您可以与组件一起导出链接
数组,以便消费者可以访问其样式。
导入此组件时,消费者现在还需要导入此 links
数组并将其附加到其路由的 links
导出:
这种方法在规则排序方面更加可预测,因为它可以让您精细地控制每个文件,并在开发和生产之间提供一致的行为。与开发期间捆绑的 CSS 相反,样式在不再需要时会从文档中删除。如果页面的 head
元素被重新安装,则由您的路由定义的任何 link
标签也将重新安装,因为它们是 React 生命周期的一部分。
这种方法的缺点是它会导致大量的样板代码。
如果您有许多可重复使用的组件,每个组件都有自己的 CSS 文件,则需要手动将每个组件的所有链接
显示到您的路由组件,这可能需要将 CSS URL 传递到多个级别的组件。这也很容易出错,因为很容易忘记导入组件的链接
数组。
尽管它有诸多优点,但与 CSS 打包相比,您可能会发现它过于繁琐,或者您可能会发现额外的样板代码是值得的。这一点没有对错之分。
结论
在 Remix 应用程序中管理 CSS 文件时,这最终取决于个人喜好,但这里有一个很好的经验法则:
- 如果您的项目只有少量 CSS 文件(例如,使用 Tailwind 时,您可能只有一个 CSS 文件),则应使用 CSS URL 导入。增加的样板很少,您的开发环境将更接近生产环境。
- 如果您的项目有大量与较小的可重复使用组件绑定的 CSS 文件,您可能会发现减少 CSS 捆绑的样板更符合人体工程学。只需注意权衡利弊,并以一种能够抵御文件顺序更改的方式编写 CSS。
- 如果您在开发过程中遇到样式消失的问题,您应该考虑使用 React 金丝雀版本,以便 React 在重新安装页面时不会删除现有的
head
元素。