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 关键资源优先 #
- 预加载关键字体
- 内联关键CSS
- 延迟非关键JavaScript
7.2 资源组织 #
text
public/
├── fonts/
│ └── inter-var.woff2
├── images/
│ ├── hero.webp
│ └── logo.svg
└── scripts/
└── analytics.js
7.3 性能检查 #
- 使用 Lighthouse 检查
- 监控 Core Web Vitals
- 检查资源加载瀑布图
八、总结 #
本章我们学习了:
- links函数:定义资源链接
- 预加载策略:intent、render、viewport
- 懒加载:组件和图片懒加载
- 资源优化:字体、图片、脚本优化
- 缓存策略:静态和动态资源缓存
核心要点:
- 预加载关键资源
- 懒加载非关键资源
- 合理使用缓存
- 监控资源加载性能
最后更新:2026-03-28