Remix资源加载 #

一、资源加载概述 #

Remix 提供了灵活的资源加载机制,可以优化页面加载性能。

二、links函数 #

2.1 样式表 #

tsx
import type { LinksFunction } from "@remix-run/node";
import styles from "./styles.css?url";

export const links: LinksFunction = () => [
  { rel: "stylesheet", href: styles },
];

2.2 预加载资源 #

tsx
export const links: LinksFunction = () => [
  {
    rel: "preload",
    href: "/fonts/inter.woff2",
    as: "font",
    type: "font/woff2",
    crossOrigin: "anonymous",
  },
  {
    rel: "preload",
    href: "/images/hero.webp",
    as: "image",
  },
];

2.3 图标 #

tsx
export const links: LinksFunction = () => [
  { rel: "icon", href: "/favicon.ico" },
  { rel: "apple-touch-icon", href: "/apple-touch-icon.png" },
];

三、预加载策略 #

3.1 链接预加载 #

tsx
import { Link } from "@remix-run/react";

export default function Navigation() {
  return (
    <nav>
      <Link to="/" prefetch="render">首页</Link>
      <Link to="/about" prefetch="intent">关于</Link>
      <Link to="/contact" prefetch="viewport">联系</Link>
    </nav>
  );
}

预加载选项:

选项 说明
none 不预加载
intent 悬停或聚焦时预加载
render 渲染时立即预加载
viewport 进入视口时预加载

3.2 手动预加载 #

tsx
import { useFetcher } from "@remix-run/react";

export function usePreload(path: string) {
  const fetcher = useFetcher();
  
  const preload = () => {
    fetcher.load(path);
  };
  
  return preload;
}

export default function Card({ post }) {
  const preload = usePreload(`/posts/${post.id}`);
  
  return (
    <div onMouseEnter={preload}>
      <Link to={`/posts/${post.id}`}>{post.title}</Link>
    </div>
  );
}

四、懒加载 #

4.1 组件懒加载 #

tsx
import { lazy, Suspense } from "react";

const HeavyComponent = lazy(() => import("~/components/Heavy"));

export default function Page() {
  return (
    <Suspense fallback={<div>加载中...</div>}>
      <HeavyComponent />
    </Suspense>
  );
}

4.2 图片懒加载 #

tsx
export function Image({ src, alt }) {
  return (
    <img 
      src={src} 
      alt={alt} 
      loading="lazy"
      decoding="async"
    />
  );
}

4.3 Intersection Observer懒加载 #

tsx
import { useEffect, useRef, useState } from "react";

export function LazyImage({ src, alt }) {
  const ref = useRef<HTMLDivElement>(null);
  const [loaded, setLoaded] = useState(false);
  
  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        if (entries[0].isIntersecting) {
          setLoaded(true);
          observer.disconnect();
        }
      },
      { rootMargin: "100px" }
    );
    
    if (ref.current) {
      observer.observe(ref.current);
    }
    
    return () => observer.disconnect();
  }, []);
  
  return (
    <div ref={ref}>
      {loaded ? (
        <img src={src} alt={alt} />
      ) : (
        <div className="placeholder" />
      )}
    </div>
  );
}

五、资源优化 #

5.1 字体优化 #

tsx
export const links: LinksFunction = () => [
  {
    rel: "preload",
    href: "/fonts/inter-var.woff2",
    as: "font",
    type: "font/woff2",
    crossOrigin: "anonymous",
  },
];

// CSS
// font-display: swap;

5.2 图片优化 #

tsx
export function OptimizedImage({ src, alt, width, height }) {
  return (
    <picture>
      <source srcSet={`${src}.webp`} type="image/webp" />
      <source srcSet={`${src}.jpg`} type="image/jpeg" />
      <img
        src={`${src}.jpg`}
        alt={alt}
        width={width}
        height={height}
        loading="lazy"
        decoding="async"
      />
    </picture>
  );
}

5.3 脚本优化 #

tsx
export const links: LinksFunction = () => [
  {
    rel: "preload",
    href: "/scripts/analytics.js",
    as: "script",
  },
];

export default function Page() {
  return (
    <div>
      <script src="/scripts/analytics.js" defer />
    </div>
  );
}

六、缓存策略 #

6.1 静态资源缓存 #

tsx
export const headers = () => ({
  "Cache-Control": "public, max-age=31536000, immutable",
});

6.2 动态资源缓存 #

tsx
export async function loader() {
  return json(data, {
    headers: {
      "Cache-Control": "public, max-age=3600, stale-while-revalidate=86400",
    },
  });
}

七、最佳实践 #

7.1 关键资源优先 #

  1. 预加载关键字体
  2. 内联关键CSS
  3. 延迟非关键JavaScript

7.2 资源组织 #

text
public/
├── fonts/
│   └── inter-var.woff2
├── images/
│   ├── hero.webp
│   └── logo.svg
└── scripts/
    └── analytics.js

7.3 性能检查 #

  • 使用 Lighthouse 检查
  • 监控 Core Web Vitals
  • 检查资源加载瀑布图

八、总结 #

本章我们学习了:

  1. links函数:定义资源链接
  2. 预加载策略:intent、render、viewport
  3. 懒加载:组件和图片懒加载
  4. 资源优化:字体、图片、脚本优化
  5. 缓存策略:静态和动态资源缓存

核心要点:

  • 预加载关键资源
  • 懒加载非关键资源
  • 合理使用缓存
  • 监控资源加载性能
最后更新:2026-03-28