导入
Astro 最佳实践
学习如何将你的 Astro 项目提升到更高水平。

相同布局的不同页面
1--- 2import SEO from '@components/SEO.astro'; 3import Header from '@components/Header.astro'; 4import Footer from '@components/Footer.astro'; 5 6import '../styles/global.css'; 7 8const { seo } = Astro.props; 9 10const hasLeftSidebar = Astro.slots.has('left-sidebar'); 11const hasRightSidebar = Astro.slots.has('right-sidebar'); 12--- 13 14<!DOCTYPE html> 15<html lang="en"> 16 <head> 17 <SEO {...seo} /> 18 </head> 19 <body> 20 <Header /> 21 {hasLeftSidebar && ( 22 <aside> 23 <slot name="left-sidebar" /> 24 </aside> 25 )} 26 <main> 27 <slot /> 28 </main> 29 {hasRightSidebar && ( 30 <aside> 31 <slot name="right-sidebar" /> 32 </aside> 33 )} 34 <Footer /> 35 </body> 36</html>
1--- 2import { seo, author } from '@config'; 3 4export interface Props { 5 title: string 6 description: string 7 publishDate: string 8 updatedDate: string 9 image?: string 10 tags: string[] 11 useArticleSchema: boolean 12} 13 14const canonicalURL = new URL(Astro.url.pathname, Astro.site); 15 16const { 17 title = seo.title, 18 description = seo.description, 19 publishDate, 20 updatedDate, 21 image = '/assets/img/logo.svg', 22 tags = ['Astro', '前端'], 23 useArticleSchema = false 24} = Astro.props; 25 26const websiteSchema = ` 27 { 28 "@context": "https://schema.org", 29 "@type": "WebSite", 30 "url": "${canonicalURL}", 31 "name": "${seo.title}", 32 "description": "${seo.description}" 33 } 34` 35 36const articleSchema = ` 37 { 38 "@context": "https://schema.org", 39 "@type": "Article", 40 "url": "${canonicalURL}", 41 "headline": "${title}", 42 "image": "${image}", 43 "description": "${description}", 44 "dateCreated": "${publishDate}", 45 "datePublished": "${publishDate}", 46 "dateModified": "${updatedDate || publishDate}", 47 "keywords": [ 48 ${tags.map((tag: string) => `"${tag}"`).join(',')} 49 ], 50 "author": { 51 "@type": "Person", 52 "name": "${author.name}", 53 "url": "${Astro.site}contact" 54 }, 55 "publisher": { 56 "@type": "Organization", 57 "name": "${seo.title}", 58 "url": "${Astro.site}" 59 } 60 } 61` 62--- 63 64<title>{title}</title> 65 66<!-- 主要元标签 --> 67<meta charset="utf-8" /> 68<meta name="viewport" content="width=device-width,initial-scale=1" /> 69<meta name="title" content={title} /> 70<meta name="description" content={description} /> 71<meta name="author" content={author.name} /> 72 73<!-- Open Graph / Facebook --> 74<meta name="og:type" content="website" /> 75<meta name="og:url" content={Astro.url} /> 76<meta name="og:title" content={title} /> 77<meta name="og:description" content={description} /> 78<meta name="og:image" content={new URL(image, Astro.url)} /> 79 80<!-- Twitter --> 81<meta name="twitter:card" content="summary_large_image" /> 82<meta name="twitter:url" content={Astro.url} /> 83<meta name="twitter:title" content={title} /> 84<meta name="twitter:description" content={description} /> 85<meta name="twitter:image" content={new URL(image, Astro.url)} /> 86<meta name="twitter:site" content="@WebtipsHQ" /> 87<meta name="twitter:creator" content="@WebtipsHQ" /> 88 89<!-- 链接 --> 90<link rel="canonical" href={canonicalURL} /> 91<link rel="icon" type="image/svg+xml" href="/assets/img/logo.svg" /> 92<link rel="manifest" href="/manifest.webmanifest" /> 93<link rel="dns-prefetch" href="https://www.googletagmanager.com/" /> 94<link rel="dns-prefetch" href="https://www.google-analytics.com/" /> 95 96<!-- 结构化数据 --> 97<script type="application/ld+json" set:html={websiteSchema} /> 98{useArticleSchema && ( 99 <script type="application/ld+json" set:html={articleSchema} /> 100)}
1--- 2const seo = { 3 useArticleSchema: true, 4 title: post.data.title, 5 description: post.data.excerpt.substring(0, 150) + '...', 6 publishDate: post.data.publishDate, 7 updatedDate: post.data.updatedDate, 8 image: post.data.heroImage, 9}; 10--- 11 12<Layout seo={seo}> 13 ... 14</Layout>
1--- 2import ConditionalWrapper from '@components/ConditionalWrapper.astro'; 3 4const { 5 link, 6 image 7} = Astro.props; 8// 仅当存在 `link` 属性时,图片才会被包裹在锚标签中 9--- 10 11<ConditionalWrapper condition={link}> 12 <a href={link} slot="wrapper">children</a> 13 <img src={image.src} /> 14</ConditionalWrapper>
1import classNames from 'classnames'; 2 3<h1 className={classNames([highlighted && 'highlighted'])}> 4 {welcome} 5</h1>
1<h1 class:list={[highlighted && 'highlighted']}> 2 {welcome} 3</h1>
1import { defineCollection, z } from 'astro:content'; 2 3const posts = defineCollection({ 4 schema: z.object({ 5 title: z.string(), 6 description: z.string(), 7 publishDate: z 8 .string() 9 .or(z.date()) 10 .transform((val) => new Date(val)), 11 updatedDate: z 12 .string() 13 .optional() 14 .transform((str) => (str ? new Date(str) : undefined)), 15 heroImage: z.string().optional(), 16 excerpt: z.string(), 17 faq: z.array(z.object({ 18 id: z.number(), 19 question: z.string(), 20 answer: z.string(), 21 open: z.boolean().optional() 22 })).optional() 23 }), 24}); 25 26export const collections = { posts };