Skip to content

实验性实时内容集合

类型:boolean
默认:false

¥Type: boolean
Default: false

Added in: astro@5.10.0

在你的项目中启用对实时内容集合的支持。

¥Enables support for live content collections in your project.

实时内容集合是一种新型的 内容收集,它在运行时而不是构建时获取数据。这使得你可以使用统一的 API 访问来自 CMS、API、数据库或其他来源的频繁更新的数据,而无需在数据更改时重建你的网站。

¥Live content collections are a new type of content collection that fetch their data at runtime rather than build time. This allows you to access frequently updated data from CMSs, APIs, databases, or other sources using a unified API, without needing to rebuild your site when the data changes.

¥Basic usage

要启用该功能,请确保你已为 按需渲染 配置适配器,并在 astro.config.mjs 文件中添加 experimental.liveContentCollections 标志:

¥To enable the feature, make sure you have an adapter configured for on-demand rendering and add the experimental.liveContentCollections flag to your astro.config.mjs file:

astro.config.mjs
{
experimental: {
liveContentCollections: true,
},
}

然后创建一个新的 src/live.config.ts 文件(如果有 src/content.config.ts 文件,则与 src/content.config.ts 文件一起创建),使用 astro:content 模块中新的 defineLiveCollection() 函数,使用 实时加载器 和可选的 schema 定义实时集合。

¥Then create a new src/live.config.ts file (alongside your src/content.config.ts if you have one) to define your live collections with a live loader and optionally a schema using the new defineLiveCollection() function from the astro:content module.

src/live.config.ts
import { defineLiveCollection } from 'astro:content';
import { storeLoader } from '@mystore/astro-loader';
const products = defineLiveCollection({
type: 'live',
loader: storeLoader({
apiKey: process.env.STORE_API_KEY,
endpoint: 'https://api.mystore.com/v1',
}),
});
export const collections = { products };

然后,你可以使用专用的 getLiveCollection()getLiveEntry() 函数访问实时数据:

¥You can then use the dedicated getLiveCollection() and getLiveEntry() functions to access your live data:

---
export const prerender = false; // Not needed in 'server' mode
import { getLiveCollection, getLiveEntry } from 'astro:content';
// Get all products
const { entries: allProducts, error } = await getLiveCollection('products');
if (error) {
// Handle error appropriately
console.error(error.message);
}
// Get products with a filter (if supported by your loader)
const { entries: electronics } = await getLiveCollection('products', { category: 'electronics' });
// Get a single product by ID (string syntax)
const { entry: product, error: productError } = await getLiveEntry('products', Astro.params.id);
if (productError) {
return Astro.redirect('/404');
}
// Get a single product with a custom query (if supported by your loader) using a filter object
const { entry: productBySlug } = await getLiveEntry('products', { slug: Astro.params.slug });
---

何时使用实时内容集合

标题部分 何时使用实时内容集合

¥When to use live content collections

实时内容集合专为频繁更改且需要在页面请求时保持更新的数据而设计。考虑在以下情况下使用它们:

¥Live content collections are designed for data that changes frequently and needs to be up-to-date when a page is requested. Consider using them when:

  • 你需要实时信息(例如,用户特定数据、当前库存水平)

  • 你希望避免频繁重建经常更改的内容

  • 你的数据经常更新(例如,最新的产品库存、价格、库存情况)

  • 你需要根据用户输入或请求参数将动态过滤器传递给数据源

  • 你正在为 CMS 构建预览功能,编辑者需要立即查看草稿内容

相反,在以下情况下使用构建时内容集合:

¥In contrast, use build-time content collections when:

  • 性能至关重要,并且你希望在构建时预渲染数据。

  • 你的数据相对静态(例如,博客文章、文档、产品描述)

  • 你希望受益于构建时优化和缓存

  • 你需要处理 MDX 或执行图片优化

  • 你的数据可以一次获取并在多个构建中重复使用

有关在实时和预加载集合之间进行选择的更多详细信息,请参阅 实验性实时集合的局限性与构建时集合的主要区别

¥See the limitations of experimental live collections and key differences from build-time collections for more details on choosing between live and preloaded collections.

¥Using live collections

你可以将 创建你自己的实时加载器 作为数据源,也可以使用以 npm 包形式分发的社区加载器。以下是如何使用示例 CMS 和电子商务加载器:

¥You can create your own live loaders for your data source, or you can use community loaders distributed as npm packages. Here’s how you could use example CMS and e-commerce loaders:

src/live.config.ts
import { defineLiveCollection } from 'astro:content';
import { cmsLoader } from '@example/cms-astro-loader';
import { productLoader } from '@example/store-astro-loader';
const articles = defineLiveCollection({
type: 'live',
loader: cmsLoader({
apiKey: process.env.CMS_API_KEY,
contentType: 'article',
}),
});
const products = defineLiveCollection({
type: 'live',
loader: productLoader({
apiKey: process.env.STORE_API_KEY,
}),
});
export const collections = { articles, authors };

然后,你可以使用统一的 API 从两个加载器获取内容:

¥You can then get content from both loaders with a unified API:

---
export const prerender = false; // Not needed in 'server' mode
import { getLiveCollection, getLiveEntry } from 'astro:content';
// Use loader-specific filters
const { entries: draftArticles } = await getLiveCollection('articles', {
status: 'draft',
author: 'john-doe',
});
// Get a specific product by ID
const { entry: product } = await getLiveEntry('products', Astro.params.slug);
---

¥Error handling

实时加载器可能会由于网络问题、API 错误或验证问题而失败。该 API 旨在明确错误处理。

¥Live loaders can fail due to network issues, API errors, or validation problems. The API is designed to make error handling explicit.

当你调用 getLiveCollection()getLiveEntry() 时,错误将是以下之一:

¥When you call getLiveCollection() or getLiveEntry(), the error will be one of:

  • 加载器定义的错误类型(如果返回错误)

  • 如果未找到条目,则返回 LiveEntryNotFoundError

  • 如果集合数据与预期模式不匹配,则返回 LiveCollectionValidationError

  • 如果缓存提示无效,则返回 LiveCollectionCacheHintError

  • 用于其他错误的 LiveCollectionError,例如加载器中抛出的未捕获错误

这些错误有一个静态 is() 方法,你可以使用它在运行时检查错误类型:

¥These errors have a static is() method that you can use to check the type of error at runtime:

---
export const prerender = false; // Not needed in 'server' mode
import { getLiveEntry, LiveEntryNotFoundError } from 'astro:content';
const { entry, error } = await getLiveEntry('products', Astro.params.id);
if (error) {
if (LiveEntryNotFoundError.is(error)) {
console.error(`Product not found: ${error.message}`);
Astro.response.status = 404;
} else {
console.error(`Error loading product: ${error.message}`);
return Astro.redirect('/500');
}
}
---

¥Creating a live loader

实时加载器是一个具有两种方法的对象:loadCollection()loadEntry()。这些方法应该能够优雅地处理错误,并返回数据或 错误 对象。

¥A live loader is an object with two methods: loadCollection() and loadEntry(). These methods should handle errors gracefully and return either data or an Error object.

标准模式是导出一个返回此加载器对象的函数,允许你传递配置选项,例如 API 密钥或端点。

¥The standard pattern is to export a function that returns this loader object, allowing you to pass configuration options like API keys or endpoints.

这是一个基本示例:

¥Here’s a basic example:

myloader.ts
import type { LiveLoader } from 'astro/loaders';
import { fetchFromCMS } from './cms-client.js';
interface Article {
id: string;
title: string;
content: string;
author: string;
}
export function articleLoader(config: { apiKey: string }): LiveLoader<Article> {
return {
name: 'article-loader',
loadCollection: async ({ filter }) => {
try {
const articles = await fetchFromCMS({
apiKey: config.apiKey,
type: 'article',
filter,
});
return {
entries: articles.map((article) => ({
id: article.id,
data: article,
})),
};
} catch (error) {
return {
error: new Error(`Failed to load articles: ${error.message}`),
};
}
},
loadEntry: async ({ filter }) => {
try {
// filter will be { id: "some-id" } when called with a string
const article = await fetchFromCMS({
apiKey: config.apiKey,
type: 'article',
id: filter.id,
});
if (!article) {
return {
error: new Error('Article not found'),
};
}
return {
id: article.id,
data: article,
};
} catch (error) {
return {
error: new Error(`Failed to load article: ${error.message}`),
};
}
},
};
}

¥Rendering content

加载器可以通过在条目中返回 rendered 属性 来添加对直接渲染内容的支持。这允许你使用 render() 函数和 <Content /> 组件 直接在页面中渲染内容。如果加载器未返回条目的 rendered 属性,则 <Content /> 组件将不渲染任何内容。

¥A loader can add support for directly rendered content by returning a rendered property in the entry. This allows you to use the render() function and <Content /> component to render the content directly in your pages. If the loader does not return a rendered property for an entry, the <Content /> component will render nothing.

myloader.ts
// ...
export function articleLoader(config: { apiKey: string }): LiveLoader<Article> {
return {
name: 'article-loader',
loadEntry: async ({ filter }) => {
try {
const article = await fetchFromCMS({
apiKey: config.apiKey,
type: 'article',
id: filter.id,
});
return {
id: article.id,
data: article,
rendered: {
// Assuming the CMS returns HTML content
html: article.htmlContent,
},
};
} catch (error) {
return {
error: new Error(`Failed to load article: ${error.message}`),
};
}
},
// ...
};
}

然后,你可以使用与内置时间集合相同的方法,在页面中渲染实时集合条目的内容和元数据。你还可以访问任意 实时加载器返回的错误,例如,当内容无法显示时,将其重写为 404 页面:

¥You can then render both content and metadata from live collection entries in pages using the same method as built-time collections. You also have access to any error returned by the live loader, for example, to rewrite to a 404 page when content cannot be displayed:

---
export const prerender = false; // Not needed in 'server' mode
import { getLiveEntry, render } from 'astro:content';
const { entry, error } = await getLiveEntry('articles', Astro.params.id);
if (error) {
return Astro.rewrite('/404');
}
const { Content } = await render(entry);
---
<h1>{entry.data.title}</h1>
<Content />

加载器中的错误处理

标题部分 加载器中的错误处理

¥Error handling in loaders

加载器应该处理所有错误,并返回一个 错误 子类。你可以创建自定义错误类型,并在需要时使用它们进行更具体的错误处理。如果加载器中抛出错误,它将被捕获并返回,并封装在 LiveCollectionError 中。你还可以创建 自定义错误类型 组件以实现正确的类型。

¥Loaders should handle all errors and return an Error subclass for errors. You can create custom error types and use them for more specific error handling if needed. If an error is thrown in the loader, it will be caught and returned, wrapped in a LiveCollectionError. You can also create custom error types for proper typing.

Astro 本身会根据加载器的响应生成一些错误:

¥Astro will generate some errors itself, depending on the response from the loader:

  • 如果 loadEntry 返回 undefined,Astro 将向用户返回 LiveEntryNotFoundError

  • 如果集合定义了模式,但数据与模式不匹配,Astro 将返回 LiveCollectionValidationError

  • 如果加载器返回无效的缓存提示,Astro 将返回 LiveCollectionCacheHintErrorcacheHint 字段是可选的,因此如果你没有返回有效数据,可以直接忽略它。

my-loader.ts
import type { LiveLoader } from 'astro/loaders';
import { MyLoaderError } from './errors.js';
export function myLoader(config): LiveLoader<MyData, undefined, undefined, MyLoaderError> {
return {
name: 'my-loader',
loadCollection: async ({ filter }) => {
// Return your custom error type
return {
error: new MyLoaderError('Failed to load', 'LOAD_ERROR'),
};
},
// ...
};
}

¥Distributing your loader

加载器可以在你的网站中定义,也可以作为单独的 npm 包定义。如果你想与社区分享你的加载器,你可以使用 使用 astro-componentastro-loader 关键字将其发布到 NPM

¥Loaders can be defined in your site or as a separate npm package. If you want to share your loader with the community, you can publish it to NPM with the astro-component and astro-loader keywords.

加载器应导出一个返回 LiveLoader 对象的函数,允许用户使用自己的设置对其进行配置。

¥The loader should export a function that returns the LiveLoader object, allowing users to configure it with their own settings.

¥Type safety

与常规内容集合一样,可以对实时集合进行类型设置,以确保数据的类型安全。支持 使用 Zod 模式,但并非定义实时集合类型的必要条件。与构建时定义的预加载集合不同,实时加载器可以选择将泛型传递给 LiveLoader 接口。你可以定义集合和条目数据的类型,以及用于查询的自定义过滤器类型和用于错误处理的自定义错误类型。

¥Like regular content collections, live collections can be typed to ensure type safety in your data. Using Zod schemas is supported, but not required to define types for live collections. Unlike preloaded collections defined at build time, live loaders can instead choose to pass generic types to the LiveLoader interface. You can define the types for your collection and entry data, as well as custom filter types for querying, and custom error types for error handling.

¥Type-safe data

实时加载器可以为其返回的数据定义类型。这使得 TypeScript 在处理组件中的数据时能够提供类型检查和自动补齐功能。

¥Live loaders can define types for the data they return. This allows TypeScript to provide type checking and autocompletion when working with the data in your components.

store-loader.ts
import type { LiveLoader } from 'astro/loaders';
import { fetchProduct, fetchCategory, type Product } from './store-client';
export function storeLoader(): LiveLoader<Product> {
// ...
}

当你使用 getLiveCollection()getLiveEntry() 时,TypeScript 会根据加载器的定义推断类型:

¥When you use getLiveCollection() or getLiveEntry(), TypeScript will infer the types based on the loader’s definition:

---
export const prerender = false; // Not needed in 'server' mode
import { getLiveEntry } from 'astro:content';
const { entry: product } = await getLiveEntry('products', '123');
// TypeScript knows product.data is of type Product
console.log(product?.data.name);
---

¥Type-safe filters

实时加载器可以为 getLiveCollection()getLiveEntry() 定义自定义过滤器类型。这启用了与你的 API 功能相匹配的类型安全查询,使用户更容易发现可用的过滤器并确保它们被正确使用。如果你在过滤器类型中包含 JSDoc 注释,用户在使用加载器时,会在 IDE 中看到这些注释作为提示。

¥Live loaders can define custom filter types for both getLiveCollection() and getLiveEntry(). This enables type-safe querying that matches your API’s capabilities, making it easier for users to discover available filters and ensure they are used correctly. If you include JSDoc comments in your filter types, the user will see these in their IDE as hints when using the loader.

store-loader.ts
import type { LiveLoader } from 'astro/loaders';
import { fetchProduct, fetchCategory, type Product } from './store-client';
interface CollectionFilter {
category?: string;
/** Minimum price to filter products */
minPrice?: number;
/** Maximum price to filter products */
maxPrice?: number;
}
interface EntryFilter {
/** Alias for `sku` */
id?: string;
slug?: string;
sku?: string;
}
export function productLoader(config: {
apiKey: string;
endpoint: string;
}): LiveLoader<Product, EntryFilter, CollectionFilter> {
return {
name: 'product-loader',
loadCollection: async ({ filter }) => {
// filter is typed as CollectionFilter
const data = await fetchCategory({
apiKey: config.apiKey,
category: filter?.category ?? 'all',
minPrice: filter?.minPrice,
maxPrice: filter?.maxPrice,
});
return {
entries: data.products.map((product) => ({
id: product.sku,
data: product,
})),
};
},
loadEntry: async ({ filter }) => {
// filter is typed as EntryFilter | { id: string }
const product = await fetchProduct({
apiKey: config.apiKey,
slug: filter.slug,
sku: filter.sku || filter.id,
});
if (!product) {
return {
error: new Error('Product not found'),
};
}
return {
id: product.sku,
entry: product,
};
},
};
}

¥Custom error types

你可以为 加载器返回的错误 创建自定义错误类型,并将其作为泛型传递以获得正确的类型:

¥You can create custom error types for errors returned by your loader and pass them as a generic to get proper typing:

my-loader.ts
class MyLoaderError extends Error {
constructor(
message: string,
public code?: string
) {
super(message);
this.name = 'MyLoaderError';
}
}
export function myLoader(config): LiveLoader<MyData, undefined, undefined, MyLoaderError> {
return {
name: 'my-loader',
loadCollection: async ({ filter }) => {
// Return your custom error type
return {
error: new MyLoaderError('Failed to load', 'LOAD_ERROR'),
};
},
// ...
};
}

当你使用 getLiveCollection()getLiveEntry() 时,TypeScript 会推断自定义错误类型,以便你进行适当的处​​理:

¥When you use getLiveCollection() or getLiveEntry(), TypeScript will infer the custom error type, allowing you to handle it appropriately:

---
export const prerender = false; // Not needed in 'server' mode
import { getLiveEntry } from 'astro:content';
const { entry, error } = await getLiveEntry('products', '123');
if (error) {
if (error.name === 'MyLoaderError') {
console.error(`Loader error: ${error.message} (code: ${error.code})`);
} else {
console.error(`Unexpected error: ${error.message}`);
}
return Astro.rewrite('/500');
}
---

¥Using Zod schemas

与构建时集合一样,你可以将 Zod 模式 与实时集合一起使用,以便在运行时验证和转换数据。当你定义一个模式时,查询集合时,它优先于 加载器的类型

¥Just like with build-time collections, you can use Zod schemas with live collections to validate and transform data at runtime. When you define a schema, it takes precedence over the loader’s types when you query the collection:

src/live.config.ts
import { z, defineLiveCollection } from 'astro:content';
import { apiLoader } from './loaders/api-loader';
const products = defineLiveCollection({
type: 'live',
loader: apiLoader({ endpoint: process.env.API_URL }),
schema: z
.object({
id: z.string(),
name: z.string(),
price: z.number(),
// Transform the API's category format
category: z.string().transform((str) => str.toLowerCase().replace(/\s+/g, '-')),
// Coerce the date to a Date object
createdAt: z.coerce.date(),
})
.transform((data) => ({
...data,
// Add a formatted price field
displayPrice: `$${data.price.toFixed(2)}`,
})),
});
export const collections = { products };

使用 Zod 模式时,验证错误会被自动捕获并返回为 AstroError 对象:

¥When using Zod schemas, validation errors are automatically caught and returned as AstroError objects:

---
export const prerender = false; // Not needed in 'server' mode
import { getLiveEntry, LiveCollectionValidationError } from 'astro:content';
const { entry, error } = await getLiveEntry('products', '123');
// You can handle validation errors specifically
if (LiveCollectionValidationError.is(error)) {
console.error(error.message);
return Astro.rewrite('/500');
}
// TypeScript knows entry.data matches your Zod schema, not the loader's type
console.log(entry?.data.displayPrice); // e.g., "$29.99"
---

¥Cache hints

实时加载器可以提供缓存提示,以帮助进行响应缓存。你可以使用这些数据发送 HTTP 缓存标头或以其他方式通知你的缓存策略。

¥Live loaders can provide cache hints to help with response caching. You can use this data to send HTTP cache headers or otherwise inform your caching strategy.

my-loader.ts
export function myLoader(config): LiveLoader<MyData> {
return {
name: 'cached-loader',
loadCollection: async ({ filter }) => {
// ... fetch data
return {
entries: data.map((item) => ({
id: item.id,
data: item,
// You can optionally provide cache hints for each entry
// These are merged with the collection's cache hint
cacheHint: {
tags: [`product-${item.id}`, `category-${item.category}`],
},
})),
cacheHint: {
tags: ['products'],
maxAge: 300, // 5 minutes
},
};
},
loadEntry: async ({ filter }) => {
// ... fetch single item
return {
id: item.id,
data: item,
cacheHint: {
tags: [`product-${item.id}`, `category-${item.category}`],
maxAge: 3600, // 1 hour
},
};
},
};
}

然后,你可以在页面中使用以下提示:

¥You can then use these hints in your pages:

---
export const prerender = false; // Not needed in 'server' mode
import { getLiveEntry } from 'astro:content';
const { entry, error, cacheHint } = await getLiveEntry('products', Astro.params.id);
if (error) {
return Astro.redirect('/404');
}
// Apply cache hints to response headers
if (cacheHint) {
Astro.response.headers.set('Cache-Tag', cacheHint.tags.join(','));
Astro.response.headers.set('Cache-Control', `s-maxage=${cacheHint.maxAge}`);
}
---
<h1>{entry.data.name}</h1>
<p>{entry.data.description}</p>

¥Live collection limitations

与构建时集合相比,实时内容集合有一些限制:

¥Live content collections have some limitations compared to build-time collections:

  • 不支持 MDX:MDX 无法在运行时渲染

  • 不支持图片优化:图片无法在运行时处理

  • 性能考虑:每次请求都会获取数据(除非已缓存)

  • 不支持数据存储持久化:数据不会保存到内容层数据存储中

与构建时集合的区别

标题部分 与构建时集合的区别

¥Differences from build-time collections

实时集合使用与当前预加载内容集合不同的 API。主要区别包括:

¥Live collections use a different API than current preloaded content collections. Key differences include:

  1. 执行时间:在请求时运行,而不是在构建时运行
  2. 配置文件:使用 src/live.config.ts 代替 src/content.config.ts
  3. 集合定义:使用 defineLiveCollection() 代替 defineCollection()
  4. 集合类型:在集合定义中设置 type: "live"
  5. 加载器 API:实现 loadCollectionloadEntry 方法,而不是 load 方法
  6. 数据返回:直接返回数据,而不是存储在数据存储中
  7. 面向用户的功能:使用 getLiveCollection/getLiveEntry 代替 getCollection/getEntry

如需完整概述并提供有关此实验性 API 的反馈,请参阅 实时内容集合 RFC

¥For a complete overview and to give feedback on this experimental API, see the Live Content collections RFC.

Astro v5.10 中文网 - 粤ICP备13048890号