Skip to content

静态资源管理

正确管理静态资源(如图片、字体、CSS 文件和其他资源)对于构建专业的文档网站至关重要。本章介绍 Rspress 如何处理静态资源以及组织它们的最佳实践。

理解静态资源

什么是静态资源?

静态资源是不需要处理并直接提供给用户的文件:

  • 图片: PNG, JPG, SVG, GIF, WebP
  • 字体: WOFF, WOFF2, TTF, OTF
  • 样式表: CSS 文件
  • 文档: PDF, ZIP 文件
  • 媒体: 视频、音频文件
  • 数据: JSON, XML 文件

资源加载策略

Rspress 提供多种处理资源的方式:

  1. Public 目录: 用于通过 URL 直接引用的资源
  2. 相对导入: 用于打包器处理的资源
  3. 外部 URL: 用于 CDN 托管的资源

Public 目录

基本用法

将资源放在 public 目录中以直接提供:

my-rspress-site/
├── docs/
├── public/
│   ├── images/
│   │   ├── logo.png
│   │   └── screenshot.png
│   ├── fonts/
│   │   └── custom-font.woff2
│   └── favicon.ico
└── rspress.config.ts

使用绝对路径引用 public 资源:

markdown
<!-- 在您的 Markdown 文件中 -->
![Logo](/images/logo.png)

<img src="/images/screenshot.png" alt="截图" />

子目录组织

按类型组织资源:

public/
├── images/
│   ├── icons/
│   │   ├── check.svg
│   │   └── cross.svg
│   ├── screenshots/
│   │   ├── dashboard.png
│   │   └── settings.png
│   └── logos/
│       ├── logo-light.png
│       └── logo-dark.png
├── downloads/
│   ├── user-guide.pdf
│   └── template.zip
└── videos/
    └── demo.mp4

Base Path 配置

如果您的网站部署到子目录:

typescript
// rspress.config.ts
export default defineConfig({
  base: '/my-docs/',  // 网站部署在 example.com/my-docs/
});

资源会自动添加前缀:

markdown
<!-- 自动变成 /my-docs/images/logo.png -->
![Logo](/images/logo.png)

图片处理

基本图片

markdown
<!-- 相对于 public 目录 -->
![替代文本](/images/photo.jpg)

<!-- 在 HTML 中使用尺寸属性 -->
<img src="/images/photo.jpg" width="600" height="400" alt="照片" />

响应式图片

使用 HTML picture 元素实现响应式图片:

markdown
<picture>
  <source media="(max-width: 768px)" srcset="/images/mobile.jpg" />
  <source media="(max-width: 1200px)" srcset="/images/tablet.jpg" />
  <img src="/images/desktop.jpg" alt="响应式图片" />
</picture>

图片优化

在添加图片之前进行优化:

bash
# 安装图片优化工具
npm install -g imagemin-cli

# 优化图片
imagemin public/images/*.{jpg,png} --out-dir=public/images/optimized

# 或使用在线工具:
# - TinyPNG: https://tinypng.com/
# - Squoosh: https://squoosh.app/
# - ImageOptim (Mac): https://imageoptim.com/

SVG 图片

SVG 图片非常适合 logo 和图标:

markdown
<!-- 内联 SVG -->
<svg width="100" height="100" viewBox="0 0 100 100">
  <circle cx="50" cy="50" r="40" fill="blue" />
</svg>

<!-- 外部 SVG 文件 -->
![图标](/images/icon.svg)

深色模式图片

为浅色/深色主题提供不同的图片:

tsx
// components/ThemedImage.tsx
import { useTheme } from 'rspress/runtime';

export function ThemedImage({ lightSrc, darkSrc, alt }) {
  const { theme } = useTheme();

  return (
    <img
      src={theme === 'dark' ? darkSrc : lightSrc}
      alt={alt}
    />
  );
}
mdx
import { ThemedImage } from '../components/ThemedImage';

<ThemedImage
  lightSrc="/images/logo-light.png"
  darkSrc="/images/logo-dark.png"
  alt="Logo"
/>

字体

自定义字体

向您的网站添加自定义字体:

public/
└── fonts/
    ├── CustomFont-Regular.woff2
    ├── CustomFont-Bold.woff2
    └── CustomFont-Italic.woff2

创建 CSS 文件:

css
/* styles/fonts.css */
@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/CustomFont-Regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/CustomFont-Bold.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

在配置中导入:

typescript
// rspress.config.ts
export default defineConfig({
  head: [
    ['link', { rel: 'stylesheet', href: '/styles/fonts.css' }],
  ],
});

Google 字体

通过 CDN 使用 Google 字体:

typescript
// rspress.config.ts
export default defineConfig({
  head: [
    [
      'link',
      {
        rel: 'stylesheet',
        href: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap',
      },
    ],
  ],
});

CSS 和样式表

全局样式

添加全局 CSS:

public/
└── styles/
    └── custom.css
css
/* public/styles/custom.css */
:root {
  --primary-color: #007bff;
  --secondary-color: #6c757d;
}

.custom-container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

.highlight {
  background-color: yellow;
  padding: 2px 4px;
}

在配置中导入:

typescript
// rspress.config.ts
export default defineConfig({
  head: [
    ['link', { rel: 'stylesheet', href: '/styles/custom.css' }],
  ],
});

组件样式

使用 CSS Modules 实现组件特定样式:

tsx
// components/Card.tsx
import styles from './Card.module.css';

export function Card({ children }) {
  return <div className={styles.card}>{children}</div>;
}
css
/* components/Card.module.css */
.card {
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 20px;
  margin: 20px 0;
}

主题覆盖

覆盖默认主题样式:

css
/* public/styles/theme-override.css */
/* 覆盖导航背景 */
.rspress-nav {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}

/* 覆盖代码块主题 */
.rspress-code-block {
  border-radius: 12px;
}

/* 覆盖侧边栏宽度 */
.rspress-sidebar {
  width: 280px;
}

可下载资源

文档文件

提供可下载的资源:

public/
└── downloads/
    ├── api-reference.pdf
    ├── quick-start-guide.pdf
    └── code-samples.zip

在文档中链接到它们:

markdown
# 资源

下载我们的资源:

- [API 参考 (PDF)](/downloads/api-reference.pdf)
- [快速入门指南 (PDF)](/downloads/quick-start-guide.pdf)
- [代码示例 (ZIP)](/downloads/code-samples.zip)

视频和音频

嵌入视频

嵌入视频文件:

markdown
<video controls width="100%">
  <source src="/videos/tutorial.mp4" type="video/mp4" />
  <source src="/videos/tutorial.webm" type="video/webm" />
  您的浏览器不支持视频播放。
</video>

YouTube/Vimeo 嵌入

markdown
<!-- YouTube -->
<iframe
  width="560"
  height="315"
  src="https://www.youtube.com/embed/VIDEO_ID"
  frameborder="0"
  allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
  allowfullscreen
></iframe>

Favicon 和 Meta 图片

Favicon

添加 favicon 文件:

public/
├── favicon.ico
├── favicon-16x16.png
├── favicon-32x32.png
└── apple-touch-icon.png

在 rspress.config.ts 中配置:

typescript
export default defineConfig({
  icon: '/favicon.ico',
  head: [
    ['link', { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png' }],
    ['link', { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png' }],
    ['link', { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png' }],
  ],
});

Open Graph 图片

添加社交媒体预览图片:

typescript
export default defineConfig({
  head: [
    ['meta', { property: 'og:image', content: '/images/og-image.png' }],
    ['meta', { property: 'og:image:width', content: '1200' }],
    ['meta', { property: 'og:image:height', content: '630' }],
    ['meta', { name: 'twitter:card', content: 'summary_large_image' }],
    ['meta', { name: 'twitter:image', content: '/images/twitter-card.png' }],
  ],
});

资源优化

图片格式

选择适当的格式:

  • JPEG: 照片、有许多颜色的复杂图片
  • PNG: 带透明度的图形、截图
  • SVG: Logo、图标、简单图形
  • WebP: 现代格式、最佳压缩(带后备)

压缩

压缩资源以加快加载速度:

bash
# JPG/PNG 压缩
imagemin public/images/*.{jpg,png} --plugin.webp --out-dir=public/images/compressed

# SVG 优化
npx svgo public/images/*.svg

懒加载

懒加载图片以获得更好的性能:

markdown
<img src="/images/large-image.jpg" loading="lazy" alt="大图" />

最佳实践

文件命名

使用一致、描述性的名称:

✅ 好的:
- dashboard-screenshot-2024.png
- user-authentication-flow.svg
- api-integration-guide.pdf

❌ 不好的:
- IMG_1234.png
- pic.svg
- download.pdf

组织

按目的或类型组织:

public/
├── images/
│   ├── features/      # 功能图片
│   ├── tutorials/     # 教程截图
│   └── ui/            # UI 元素
├── downloads/
│   ├── guides/        # 用户指南
│   └── samples/       # 代码示例
└── media/
    ├── videos/        # 视频内容
    └── audio/         # 音频内容

文件大小

保持合理的文件大小:

  • 图片: < 200KB (优化较大的图片)
  • 文档: < 10MB
  • 视频: 对大视频使用流媒体服务

故障排除

图片未加载

  1. 检查文件路径: 确保路径正确
markdown
<!-- 错误 -->
![图片](images/photo.jpg)

<!-- 正确 -->
![图片](/images/photo.jpg)
  1. 验证文件存在: 检查 public 目录

  2. 检查 base path: 确保 base path 配置与部署路径匹配

大文件大小

bash
# 检查文件大小
du -h public/images/*

# > 1MB 的文件应该优化
find public -type f -size +1M

性能提示

优化加载

  1. 使用 WebP 格式并带后备
  2. 对折叠下方的图片实施懒加载
  3. 使用适当的尺寸(不要为缩略图加载 4K 图片)
  4. 在服务器上启用压缩
  5. 为静态资源使用 CDN

下一步


最佳实践

  • 在添加图片之前优化它们
  • 使用适当的图片格式
  • 为可访问性提供替代文本
  • 逻辑地组织资源
  • 考虑为大图片使用懒加载

文件大小

  • 尽可能将图片保持在 200KB 以下
  • 在上传前压缩 PDF
  • 对大视频使用视频流服务
  • 在慢速连接上测试加载时间

工具