Astro 渲染上下文
当渲染一个页面时,Astro 会提供一个针对当前渲染的运行时 API。这包括当前页面 URL 等有用信息,以及执行重定向到另一个页面等操作的 API。
在 .astro
组件中,此上下文可从 Astro
全局对象中获得。端点函数也会将此相同的上下文对象作为其第一个参数来调用,其属性与 Astro 全局属性相对应。
某些属性仅在按需渲染的路由中可用,或者在预渲染页面上的功能可能受限。
Astro
全局对象可用于所有 .astro
文件。在端点函数中使用 context
对象来提供静态或实时服务器端点,并在中间件中使用它来在页面或端点即将渲染时注入行为。
上下文对象
标题为“上下文对象”的部分以下属性在 Astro
全局对象上可用(例如 Astro.props
、Astro.redirect()
),也存在于传递给端点函数和中间件的上下文对象上(例如 context.props
、context.redirect()
)。
props
标题为“props”的部分props
是一个对象,包含作为组件属性传递的任何值。
---const { title, date } = Astro.props;---<div> <h1>{title}</h1> <p>{date}</p></div>
---import Heading from '../components/Heading.astro';---<Heading title="My First Post" date="09 Aug 2022" />
props
对象还包含在渲染静态路由时从 getStaticPaths()
传递的任何 props
。
---export function getStaticPaths() { return [ { params: { id: '1' }, props: { author: 'Blu' } }, { params: { id: '2' }, props: { author: 'Erika' } }, { params: { id: '3' }, props: { author: 'Matthew' } } ];}
const { id } = Astro.params;const { author } = Astro.props;---
import type { APIContext } from 'astro';
export function getStaticPaths() { return [ { params: { id: '1' }, props: { author: 'Blu' } }, { params: { id: '2' }, props: { author: 'Erika' } }, { params: { id: '3' }, props: { author: 'Matthew' } } ];}
export function GET({ props }: APIContext) { return new Response( JSON.stringify({ author: props.author }), );}
另请参阅:使用 props
传递数据
params
标题为“params”的部分params
是一个对象,包含请求匹配到的动态路由段的值。其键必须与页面或端点文件路径中的参数匹配。
在静态构建中,这将是 getStaticPaths()
返回的用于预渲染动态路由的 params
。
---export function getStaticPaths() { return [ { params: { id: '1' } }, { params: { id: '2' } }, { params: { id: '3' } } ];}const { id } = Astro.params;---<h1>{id}</h1>
import type { APIContext } from 'astro';
export function getStaticPaths() { return [ { params: { id: '1' } }, { params: { id: '2' } }, { params: { id: '3' } } ];}
export function GET({ params }: APIContext) { return new Response( JSON.stringify({ id: params.id }), );}
当路由按需渲染时,params
可以是与动态路由模式中的路径段匹配的任何值。
---import { getPost } from '../api';
const post = await getPost(Astro.params.id);
// No posts found with this IDif (!post) { return Astro.redirect("/404")}---<html> <h1>{post.name}</h1></html>
另请参阅:params
url
标题为“url”的部分类型: URL
url
是一个根据当前 request.url
值构造的 URL 对象。它对于与请求 URL 的各个属性(如路径名和来源)进行交互非常有用。
Astro.url
等同于 new URL(Astro.request.url)
。
在开发模式下,url
将是一个 localhost
URL。构建站点时,预渲染的路由将根据site
和base
选项接收一个 URL。如果未配置 site
,预渲染的页面在构建期间也将收到一个 localhost
URL。
<h1>The current URL is: {Astro.url}</h1><h1>The current URL pathname is: {Astro.url.pathname}</h1><h1>The current URL origin is: {Astro.url.origin}</h1>
你也可以通过将 url
作为参数传递给 new URL()
来创建新的 URL。
---// Example: Construct a canonical URL using your production domainconst canonicalURL = new URL(Astro.url.pathname, Astro.site);// Example: Construct a URL for SEO meta tags using your current domainconst socialImageURL = new URL('/images/preview.png', Astro.url);---<link rel="canonical" href={canonicalURL} /><meta property="og:image" content={socialImageURL} />
site
标题为“site”的部分类型: URL | undefined
site
返回一个根据你的 Astro 配置中的 site
创建的 URL
。如果在你的 Astro 配置中没有为site
设置值,它将返回 undefined
。
<link rel="alternate" type="application/rss+xml" title="Your Site's Title" href={new URL("rss.xml", Astro.site)}/>
clientAddress
标题为“clientAddress”的部分类型: string
clientAddress
指定请求的IP 地址。此属性仅在按需渲染的路由中可用,不能在预渲染页面上使用。
---export const prerender = false; // Not needed in 'server' mode---
<div>Your IP address is: <span class="address">{Astro.clientAddress}</span></div>
export const prerender = false; // Not needed in 'server' modeimport type { APIContext } from 'astro';
export function GET({ clientAddress }: APIContext) { return new Response(`Your IP address is: ${clientAddress}`);}
isPrerendered
标题为“isPrerendered”的部分类型:boolean
astro@5.0.0
一个布尔值,表示当前页面是否为预渲染。
你可以在中间件中使用此属性来运行条件逻辑,例如,避免在预渲染页面中访问头部信息。
generator
标题为“generator”的部分类型: string
generator
提供了你的项目正在运行的当前 Astro 版本。这是一种方便的方式,可以用来添加一个带有当前 Astro 版本的 <meta name="generator">
标签。其格式为 "Astro v5.x.x"
。
<html> <head> <meta name="generator" content={Astro.generator} /> </head> <body> <footer> <p>Built with <a href="https://astro.js.cn">{Astro.generator}</a></p> </footer> </body></html>
import type { APIContext } from 'astro';
export function GET({ generator, site }: APIContext) { const body = JSON.stringify({ generator, site }); return new Response(body);}
request
标题为“request”的部分类型: Request
request
是一个标准的 Request 对象。它可以用来获取请求的 url
、headers
、method
甚至请求体。
<p>Received a {Astro.request.method} request to "{Astro.request.url}".</p><p>Received request headers:</p><p><code>{JSON.stringify(Object.fromEntries(Astro.request.headers))}</code></p>
import type { APIContext } from 'astro';
export function GET({ request }: APIContext) { return new Response(`Hello ${request.url}`);}
在预渲染页面上,request.url
不包含搜索参数,如 ?type=new
,因为在静态构建期间无法提前确定它们。但是,对于按需渲染的页面,request.url
确实包含搜索参数,因为它们可以从服务器请求中确定。
response
标题为“response”的部分类型: ResponseInit & { readonly headers: Headers }
response
是一个标准的 ResponseInit
对象。它具有以下结构。
status
:响应的数字状态码,例如200
。statusText
:与状态码关联的状态消息,例如'OK'
。headers
:一个Headers
实例,你可以用它来设置响应的 HTTP 头部。
Astro.response
用于设置页面响应的 status
、statusText
和 headers
。
---if (condition) { Astro.response.status = 404; Astro.response.statusText = 'Not found';}---
或者设置一个头部
---Astro.response.headers.set('Set-Cookie', 'a=b; Path=/;');---
redirect()
标题为“redirect()”的部分类型: (path: string, status?: number) => Response
redirect()
返回一个 Response 对象,允许你重定向到另一个页面,并可选择性地提供一个 HTTP 响应状态码作为第二个参数。
一个页面(而不是子组件)必须 return
Astro.redirect()
的结果,重定向才会发生。
对于静态生成的路由,这将使用 <meta http-equiv="refresh">
标签产生一个客户端重定向,并且不支持状态码。
对于按需渲染的路由,重定向时支持设置自定义状态码。如果未指定,重定向将以 302
状态码提供服务。
以下示例将用户重定向到登录页面
---import { isLoggedIn } from '../utils';
const cookie = Astro.request.headers.get('cookie');
// If the user is not logged in, redirect them to the login pageif (!isLoggedIn(cookie)) { return Astro.redirect('/login');}---
<p>User information</p>
import type { APIContext } from 'astro';
export function GET({ redirect, request }: APIContext) { const cookie = request.headers.get('cookie'); if (!isLoggedIn(cookie)) { return redirect('/login', 302); } else { // return user information }}
rewrite()
标题为“rewrite()”的部分类型: (rewritePayload: string | URL | Request) => Promise<Response>
astro@4.13.0
rewrite()
允许你从不同的 URL 或路径提供内容,而无需将浏览器重定向到新页面。
该方法接受字符串、URL
或 Request
作为路径的位置。
使用字符串提供显式路径
---return Astro.rewrite("/login")---
import type { APIContext } from 'astro';
export function GET({ rewrite }: APIContext) { return rewrite('/login');}
当你需要为重写构建 URL 路径时,使用 URL
类型。以下示例通过从相对路径 "../"
创建一个新 URL 来渲染页面的父路径
---return Astro.rewrite(new URL("../", Astro.url))---
import type { APIContext } from 'astro';
export function GET({ rewrite }: APIContext) { return rewrite(new URL("../", Astro.url));}
使用 Request
类型可以完全控制发送到服务器以获取新路径的 Request
。以下示例发送一个请求来渲染父页面,同时还提供了头部信息
---return Astro.rewrite(new Request(new URL("../", Astro.url), { headers: { "x-custom-header": JSON.stringify(Astro.locals.someValue) }}))---
import type { APIContext } from 'astro';
export function GET({ rewrite }: APIContext) { return rewrite(new Request(new URL("../", Astro.url), { headers: { "x-custom-header": JSON.stringify(Astro.locals.someValue) } }));}
originPathname
标题为“originPathname”的部分类型: string
astro@5.0.0
originPathname
定义了请求的原始路径名,在应用重写之前。
<p>The origin path is {Astro.originPathname}</p><p>The rewritten path is {Astro.url.pathname}</p>
import { defineMiddleware } from 'astro:middleware';
export const onRequest = defineMiddleware(async (context, next) => { // Record the original pathname before any rewrites recordPageVisit(context.originPathname); return next();});
locals
标题为“locals”的部分添加于: `astro@2.4.0`
locals
是一个对象,用于在请求的生命周期内存储和访问任意信息。 Astro.locals
是一个对象,包含由中间件设置的 context.locals
对象中的任何值。用它在你的 .astro
文件中访问由中间件返回的数据。
中间件函数可以读取和写入 context.locals
的值
import type { MiddlewareHandler } from 'astro';
export const onRequest: MiddlewareHandler = ({ locals }, next) => { if (!locals.title) { locals.title = "Default Title"; } return next();}
Astro 组件和 API 端点在渲染时可以从 locals
读取值
---const title = Astro.locals.title;---<h1>{title}</h1>
import type { APIContext } from 'astro';
export function GET({ locals }: APIContext) { return new Response(locals.title); // "Default Title"}
preferredLocale
标题为“preferredLocale”的部分类型: string | undefined
astro@3.5.0
preferredLocale
是一个计算值,用于在访问者的浏览器语言偏好和你的站点支持的区域设置之间找到最佳匹配。
它是通过检查你的 i18n.locales
数组中配置的区域设置以及用户浏览器通过 Accept-Language
头部支持的区域设置来计算的。如果不存在这样的匹配,则此值为 undefined
。
此属性仅在按需渲染的路由中可用,不能在预渲染的静态页面上使用。
preferredLocaleList
标题为“preferredLocaleList”的部分类型: string[] | undefined
astro@3.5.0
preferredLocaleList
表示浏览器请求且你的网站支持的所有区域设置的数组。这将生成你的站点和访问者之间所有兼容语言的列表。
如果在你的区域设置数组中找不到浏览器请求的任何语言,则该值为 []
。当你不支持访问者的任何首选区域设置时,会发生这种情况。
如果浏览器未指定任何首选语言,则此值将是 i18n.locales
:你所有支持的区域设置将被视为没有偏好的访问者同等偏好。
此属性仅在按需渲染的路由中可用,不能在预渲染的静态页面上使用。
currentLocale
标题为“currentLocale”的部分类型: string | undefined
根据当前 URL 计算出的区域设置,使用你在 locales
配置中指定的语法。如果 URL 不包含 /[locale]/
前缀,则该值将默认为 i18n.defaultLocale
。
getActionResult()
标题为“getActionResult()”的部分类型: (action: TAction) => ActionReturnType<TAction> | undefined
astro@4.15.0
getActionResult()
是一个函数,它返回一个Action提交的结果。它接受一个 action 函数作为参数(例如 actions.logout
),并在收到提交时返回一个 data
或 error
对象。否则,它将返回 undefined
。
---import { actions } from 'astro:actions';
const result = Astro.getActionResult(actions.logout);---
<form action={actions.logout}> <button type="submit">Log out</button></form>{result?.error && <p>Failed to log out. Please try again.</p>}
callAction()
标题为“callAction()”的部分
新增于: astro@4.15.0
callAction()
是一个用于直接从你的 Astro 组件调用 Action 处理程序的函数。该函数接受一个 Action 函数作为第一个参数(例如 actions.logout
),以及该 action 接收的任何输入作为第二个参数。它以 promise 的形式返回 action 的结果。
---import { actions } from 'astro:actions';
const { data, error } = await Astro.callAction(actions.logout, { userId: '123' });---
routePattern
标题为“routePattern”的部分类型:string
astro@5.0.0
负责生成当前页面或路由的路由模式。在基于文件的路由中,这类似于你项目中用于创建路由的文件路径。当集成插件为你的项目创建路由时,context.routePattern
与 injectRoute.pattern
的值相同。
该值将以斜杠开头,并且看起来类似于相对于你的 src/pages/
文件夹的页面组件路径,不带文件扩展名。
例如,文件 src/pages/en/blog/[slug].astro
将为 routePattern
返回 /en/blog/[slug]
。由该文件生成的你网站上的每个页面(例如 /en/blog/post-1/
、/en/blog/post-2/
等)都共享相同的 routePattern
值。对于 index.*
路由,路由模式将不包括“index”一词。例如,src/pages/index.astro
将返回 /
。
你可以使用此属性来了解哪个路由正在渲染你的组件。这允许你将类似生成的页面 URL 一起定位或分析。例如,你可以用它来有条件地渲染某些信息,或收集关于哪些路由较慢的指标。
cookies
标题为“cookies”的部分类型: AstroCookies
cookies
包含用于为按需渲染的路由读取和操作 cookie 的实用工具。
Cookie 实用工具
标题为“Cookie 实用工具”的部分cookies.get()
标题为“cookies.get()”的部分类型: (key: string, options?: AstroCookieGetOptions) => AstroCookie | undefined
将 cookie 作为 AstroCookie
对象获取,该对象包含 value
和用于将 cookie 转换为非字符串类型的实用函数。
cookies.has()
标题为“cookies.has()”的部分类型: (key: string, options?: AstroCookieGetOptions) => boolean
此 cookie 是否存在。如果 cookie已通过 Astro.cookies.set()
设置,则此方法将返回 true,否则,它将检查 Astro.request
中的 cookie。
cookies.set()
标题为“cookies.set()”的部分类型: (key: string, value: string | object, options?: AstroCookieSetOptions) => void
将 cookie key
设置为给定的值。这将尝试将 cookie 值转换为字符串。选项提供了设置cookie 功能的方法,例如 maxAge
或 httpOnly
。
cookies.delete()
标题为“cookies.delete()”的部分类型: (key: string, options?: AstroCookieDeleteOptions) => void
通过将过期日期设置为过去的时间(Unix 时间中的 0)来使 cookie 失效。
一旦 cookie 被“删除”(过期),Astro.cookies.has()
将返回 false
,而 Astro.cookies.get()
将返回一个值为 undefined
的 AstroCookie
。删除 cookie 时可用的选项有:domain
、path
、httpOnly
、sameSite
和 secure
。
cookies.merge()
标题为“cookies.merge()”的部分类型: (cookies: AstroCookies) => void
将一个新的 AstroCookies
实例合并到当前实例中。任何新的 cookie 都将被添加到当前实例中,任何同名的 cookie 将覆盖现有值。
cookies.headers()
标题为“cookies.headers()”的部分类型: () => Iterator<string>
获取将随响应一起发送的 Set-Cookie
的头部值。
AstroCookie
类型
标题为“AstroCookie 类型”的部分通过 Astro.cookies.get()
获取 cookie 时返回的类型。它具有以下属性
value
标题为“value”的部分类型: string
cookie 的原始字符串值。
json
标题为“json”的部分类型: () => Record<string, any>
通过 JSON.parse()
解析 cookie 值,返回一个对象。如果 cookie 值不是有效的 JSON,则会抛出错误。
number
标题为“number”的部分类型: () => number
将 cookie 值解析为数字。如果不是有效数字,则返回 NaN。
boolean
标题为“boolean”的部分类型: () => boolean
将 cookie 值转换为布尔值。
AstroCookieGetOptions
标题为“AstroCookieGetOptions”的部分
添加于: astro@4.1.0
AstroCookieGetOption
接口允许你在获取 cookie 时指定选项。
decode
标题为“decode”的部分类型: (value: string) => string
允许自定义 cookie 如何反序列化为值。
AstroCookieSetOptions
标题为“AstroCookieSetOptions”的部分
添加于: astro@4.1.0
AstroCookieSetOptions
是一个对象,可以在设置 cookie 时传递给 Astro.cookies.set()
,以自定义 cookie 的序列化方式。
domain
标题为“domain”的部分类型: string
指定域。如果未设置域,大多数客户端会将其解释为应用于当前域。
expires
标题为“expires”的部分类型: Date
指定 cookie 将过期的日期。
httpOnly
标题为“httpOnly”的部分类型: boolean
如果为 true,cookie 将无法在客户端访问。
maxAge
标题为“maxAge”的部分类型: number
指定 cookie 有效的秒数。
path
标题为“path”的部分类型: string
指定 cookie 应用的域的子路径。
sameSite
标题为“sameSite”的部分类型: boolean | 'lax' | 'none' | 'strict'
指定 SameSite cookie 头部的值。
secure
标题为“secure”的部分类型: boolean
如果为 true,cookie 仅在 https 站点上设置。
encode
标题为“encode”的部分类型: (value: string) => string
允许自定义 cookie 的序列化方式。
session
标题为“session”的部分类型: AstroSession
astro@5.7.0
session
是一个对象,允许在按需渲染的路由的请求之间存储数据。它与一个只包含会话 ID 的 cookie 相关联:数据本身不存储在 cookie 中。
会话在首次使用时创建,并且会自动设置会话 cookie。如果未配置会话存储,或者当前路由是预渲染的,则 session
对象为 undefined
,并且如果你尝试使用它,将会记录一个错误。
有关如何在你的 Astro 项目中使用会话的更多信息,请参阅会话指南。
get()
标题为“get()”的部分类型:(key: string) => Promise<any>
返回会话中给定键的值。如果键不存在,则返回 undefined
。
---const cart = await Astro.session?.get('cart');---<button>🛒 {cart?.length}</button>
import type { APIContext } from 'astro';
export async function GET({ session }: APIContext) { const cart = await session.get('cart'); return Response.json({ cart });}
set()
标题为“set()”的部分类型:(key: string, value: any, options?: { ttl: number }) => void
在会话中设置给定键的值。该值可以是任何可序列化的类型。此方法是同步的,值可立即检索,但在请求结束前不会保存到后端。
---const { slug } = Astro.params;Astro.session?.set('lastViewedProduct', slug);---
import type { APIContext } from 'astro';
export async function POST({ session, request }: APIContext) { const cart = await session.get('cart'); const newItem = await request.json(); cart.push(newItem); // Save the updated cart to the session session.set('cart', cart); return Response.json({ cart });}
regenerate()
标题为“regenerate()”的部分类型:() => void
重新生成会话 ID。当用户登录或提升其权限时调用此方法,以防止会话固定攻击。
---Astro.session?.regenerate();---
import type { APIContext } from 'astro';
export async function POST({ session }: APIContext) { // Authenticate the user... doLogin(); // Regenerate the session ID to prevent session fixation attacks session.regenerate(); return Response.json({ success: true });}
destroy()
标题为“destroy()”的部分类型:() => void
销毁会话,从后端删除 cookie 和会话对象。当用户注销或其会话因其他原因失效时调用此方法。
---Astro.session?.destroy();return Astro.redirect('/login');---
import type { APIContext } from 'astro';
export async function POST({ session }: APIContext) { session.destroy(); return Response.json({ success: true });}
load()
标题为“load()”的部分类型:(id: string) => Promise<void>
通过 ID 加载会话。在正常使用中,会话会自动从请求的 cookie 中加载。使用此方法可以从不同的 ID 加载会话。如果你正在自己处理会话 ID,或者想在不使用 cookie 的情况下跟踪会话,这将非常有用。
---// Load the session from a header instead of cookiesconst sessionId = Astro.request.headers.get('x-session-id');await Astro.session?.load(sessionId);const cart = await Astro.session?.get('cart');---<h1>Your cart</h1><ul> {cart?.map((item) => ( <li>{item.name}</li> ))}</ul>
import type { APIRoute } from 'astro';
export const GET: APIRoute = async ({ session, request }) => { // Load the session from a header instead of cookies const sessionId = request.headers.get('x-session-id'); await session.load(sessionId); const cart = await session.get('cart'); return Response.json({ cart });};
已弃用的对象属性
标题为“已弃用的对象属性”的部分Astro.glob()
标题为“Astro.glob()”的部分使用 Vite 的 import.meta.glob
来查询项目文件。
Astro.glob('../pages/post/*.md')
可以替换为
Object.values(import.meta.glob('../pages/post/*.md', { eager: true }));
更多信息和用法请参见导入指南。
Astro.glob()
是一种将多个本地文件加载到静态网站设置中的方法。
---const posts = await Astro.glob('../pages/post/*.md'); // returns an array of posts that live at ./src/pages/post/*.md---
<div>{posts.slice(0, 3).map((post) => ( <article> <h2>{post.frontmatter.title}</h2> <p>{post.frontmatter.description}</p> <a href={post.url}>Read more</a> </article>))}</div>
.glob()
只接受一个参数:你想要导入的本地文件的相对 URL glob 模式。它是异步的,并返回一个包含匹配文件导出内容的数组。
.glob()
不能接受变量或内插变量的字符串,因为它们无法被静态分析。(解决方法请参见导入指南。)这是因为 Astro.glob()
是 Vite 的 import.meta.glob()
的一个包装器。
你也可以在你的 Astro 项目中直接使用 import.meta.glob()
。在以下情况下你可能会想这么做:
- 你需要在非
.astro
文件(例如 API 路由)中使用此功能。Astro.glob()
仅在.astro
文件中可用,而import.meta.glob()
在项目中的任何地方都可用。 - 你不想立即加载每个文件。
import.meta.glob()
可以返回导入文件内容的函数,而不是直接返回内容本身。请注意,这种导入方式会包含所有导入文件的样式和脚本。无论文件是否实际被使用,这些样式和脚本都将被打包并添加到页面中,因为这是由静态分析决定的,而不是在运行时决定的。 - 你希望访问每个文件的路径。
import.meta.glob()
返回一个从文件路径到其内容的映射,而Astro.glob()
返回一个内容列表。 - 你希望传递多个模式;例如,你想添加一个“否定模式”来过滤掉某些文件。
import.meta.glob()
可以选择性地接受一个 glob 字符串数组,而不是单个字符串。
在 Vite 文档中阅读更多信息。
Markdown 文件
标题为“Markdown 文件”的部分使用 Astro.glob()
加载的 Markdown 文件返回以下 MarkdownInstance
接口
export interface MarkdownInstance<T extends Record<string, any>> { /* Any data specified in this file's YAML/TOML frontmatter */ frontmatter: T; /* The absolute file path of this file */ file: string; /* The rendered path of this file */ url: string | undefined; /* Astro Component that renders the contents of this file */ Content: AstroComponentFactory; /** (Markdown only) Raw Markdown file content, excluding layout HTML and YAML/TOML frontmatter */ rawContent(): string; /** (Markdown only) Markdown file compiled to HTML, excluding layout HTML */ compiledContent(): string; /* Function that returns an array of the h1...h6 elements in this file */ getHeadings(): Promise<{ depth: number; slug: string; text: string }[]>; default: AstroComponentFactory;}
你可以选择性地使用 TypeScript 泛型为 frontmatter
变量提供类型。
---interface Frontmatter { title: string; description?: string;}const posts = await Astro.glob<Frontmatter>('../pages/post/*.md');---
<ul> {posts.map(post => <li>{post.frontmatter.title}</li>)}</ul>
Astro 文件
标题为“Astro 文件”的部分Astro 文件具有以下接口
export interface AstroInstance { /* The file path of this file */ file: string; /* The URL for this file (if it is in the pages directory) */ url: string | undefined; default: AstroComponentFactory;}
其他文件
标题为“其他文件”的部分其他文件可能有各种不同的接口,但如果你确切知道某个无法识别的文件类型包含什么内容,Astro.glob()
接受一个 TypeScript 泛型。
---interface CustomDataFile { default: Record<string, any>;}const data = await Astro.glob<CustomDataFile>('../data/**/*.js');---