如果你曾经在 app/root.tsx 里直接写 favicon <link> 标签,结果被 Remix 去重甚至悄悄丢掉——正确做法是用 links 导出。Remix v2 期望 HTML link 描述符就放在这里,框架会自动把它们渲染到文档 head 里。完整 API 见官方 Remix route links 文档。

前置条件

你需要一个能跑起来的 Remix v2 项目,外加一组 favicon 文件。要凑齐现代浏览器需要的那一整套,建议准备:

放进 public/ 的任何东西都会从站点根路径直接对外提供,所以 /public/favicon.svg 实际就是 https://yoursite.com/favicon.svg,不需要额外路由。

步骤 1:在 root.tsx 里写 links 导出

links 导出是一个返回 <link> 描述符数组的函数。Remix 会把每个描述符映射成文档 head 里的真实 HTML 标签。为了拿到正确的类型,从 @remix-run/node(或你用的运行时对应的包)引入 LinksFunction。

import type { LinksFunction } from '@remix-run/node';

export const links: LinksFunction = () =&gt; [
  { rel: 'icon', href: '/favicon.svg', type: 'image/svg+xml' },
  { rel: 'alternate icon', href: '/favicon.ico', type: 'image/x-icon' },
];

rel 的取值直接对应 HTML 规范——完整词表见 whatwg 的 rel=icon 定义。浏览器会按顺序挑第一个匹配当前上下文的图标,所以要把清晰的 SVG 排在 ICO 兜底之前。

步骤 2:补上 Apple Touch 和 manifest

不管资源类型是什么,Remix 对每个描述符一视同仁。直接堆到同一个数组里:

export const links: LinksFunction = () =&gt; [
  { rel: 'icon', href: '/favicon.svg', type: 'image/svg+xml' },
  { rel: 'alternate icon', href: '/favicon.ico', type: 'image/x-icon' },
  { rel: 'apple-touch-icon', href: '/apple-touch-icon.png', sizes: '180x180' },
  { rel: 'manifest', href: '/site.webmanifest' },
];

sizes 属性只是描述性的——浏览器在挑选最合适的图标时会参考它。如果你想了解 Remix 导出之外的 favicon link 标签写法,可以看我们的 favicon HTML 代码示例。

步骤 3:优雅地处理深色模式 SVG

浏览器确实会读取 icon link 标签上的 media。你可以准备两个 SVG,让浏览器根据用户的色彩模式自动挑选。把第二个文件存成 /public/favicon-dark.svg,然后加上两个描述符:

{
  rel: 'icon',
  href: '/favicon.svg',
  type: 'image/svg+xml',
  media: '(prefers-color-scheme: light)',
},
{
  rel: 'icon',
  href: '/favicon-dark.svg',
  type: 'image/svg+xml',
  media: '(prefers-color-scheme: dark)',
},

HTML 规范明确支持 icon link 上的 media 描述符,所以这是一等公民方案,不是野路子。SVG 这一侧如何配置才能让两个文件正确切换,详见我们的 favicon 深色模式指南,里面把媒体查询的写法讲得比较细。

步骤 4:更新图标时打破缓存

浏览器对 favicon.ico 缓存得相当激进,而 Remix 默认不会给 public/ 下的资源加内容哈希。发布新设计时,给 URL 拼一个查询字符串:

{ rel: 'icon', href: '/favicon.svg?v=2', type: 'image/svg+xml' }

每次发布新图标就把 v 加一。绑定 URL 的缓存会跟着刷新,用户标签页立刻就是新图标。

常见坑

通过 Remix 的 links 导出接入图标时,留意下面这些:

收尾

把 favicon HTML 标签接入 Remix,links 导出是最干净的方式——类型有保障、SSR 输出可预测、换图标时只改一个文件。再搭一个来自 Mzu favicondl 的清晰矢量源,加上缓存破坏查询参数,你的 Remix 应用在每个浏览器标签页里都会显得很专业。