进行中

Astro New

基于Astro框架的现代化Web应用,探索最新的静态站点生成技术

技术栈

AstroTypeScriptCSSJavaScript

Astro New

项目概述

这是一个基于Astro框架的现代化Web应用项目,旨在探索和实践最新的静态站点生成技术。项目展示了Astro的核心特性,包括组件岛架构、多框架集成和优化的构建性能。

技术特色

🚀 Astro核心特性

  • 零JavaScript默认: 默认生成纯HTML,按需加载JavaScript
  • 组件岛架构: 独立的交互式组件
  • 多框架支持: React、Vue、Svelte等框架集成
  • 优化构建: 自动代码分割和优化

🏗️ 项目架构

src/
├── components/          # 可复用组件
│   ├── ui/             # UI基础组件
│   ├── layout/         # 布局组件
│   └── interactive/    # 交互式组件
├── pages/              # 页面路由
├── layouts/            # 页面布局
├── styles/             # 样式文件
└── utils/              # 工具函数

核心功能实现

1. 组件系统设计

---
// BaseCard.astro - 基础卡片组件
export interface Props {
  title: string;
  description?: string;
  image?: string;
  href?: string;
  class?: string;
}

const { title, description, image, href, class: className } = Astro.props;
---

<div class={`card ${className || ''}`}>
  {image && (
    <div class="card-image">
      <img src={image} alt={title} loading="lazy" />
    </div>
  )}
  
  <div class="card-content">
    <h3 class="card-title">{title}</h3>
    {description && (
      <p class="card-description">{description}</p>
    )}
    
    <slot />
    
    {href && (
      <a href={href} class="card-link">
        了解更多 →
      </a>
    )}
  </div>
</div>

<style>
  .card {
    background: white;
    border-radius: 12px;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
    overflow: hidden;
    transition: transform 0.3s ease, box-shadow 0.3s ease;
  }
  
  .card:hover {
    transform: translateY(-5px);
    box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15);
  }
  
  .card-image img {
    width: 100%;
    height: 200px;
    object-fit: cover;
  }
  
  .card-content {
    padding: 1.5rem;
  }
  
  .card-title {
    font-size: 1.25rem;
    font-weight: 600;
    margin-bottom: 0.5rem;
    color: #1f2937;
  }
  
  .card-description {
    color: #6b7280;
    line-height: 1.6;
    margin-bottom: 1rem;
  }
  
  .card-link {
    color: #2563eb;
    text-decoration: none;
    font-weight: 500;
    transition: color 0.3s ease;
  }
  
  .card-link:hover {
    color: #1d4ed8;
  }
</style>

2. 交互式组件集成

---
// InteractiveCounter.astro - React组件集成示例
---

<div id="counter-container">
  <h3>交互式计数器</h3>
  <div id="react-counter"></div>
</div>

<script>
  import { createRoot } from 'react-dom/client';
  import { useState } from 'react';
  
  function Counter() {
    const [count, setCount] = useState(0);
    
    return (
      <div className="counter">
        <button 
          onClick={() => setCount(count - 1)}
          className="counter-btn"
        >
          -
        </button>
        <span className="counter-value">{count}</span>
        <button 
          onClick={() => setCount(count + 1)}
          className="counter-btn"
        >
          +
        </button>
      </div>
    );
  }
  
  const container = document.getElementById('react-counter');
  if (container) {
    const root = createRoot(container);
    root.render(<Counter />);
  }
</script>

<style>
  .counter {
    display: flex;
    align-items: center;
    gap: 1rem;
    justify-content: center;
    margin: 1rem 0;
  }
  
  .counter-btn {
    width: 40px;
    height: 40px;
    border: none;
    border-radius: 8px;
    background: #2563eb;
    color: white;
    font-size: 1.2rem;
    cursor: pointer;
    transition: background-color 0.3s ease;
  }
  
  .counter-btn:hover {
    background: #1d4ed8;
  }
  
  .counter-value {
    font-size: 1.5rem;
    font-weight: 600;
    min-width: 3rem;
    text-align: center;
  }
</style>

3. 动态内容管理

// src/utils/content.ts - 内容管理工具
export interface BlogPost {
  slug: string;
  title: string;
  description: string;
  publishDate: Date;
  tags: string[];
  draft: boolean;
}

export async function getAllPosts(): Promise<BlogPost[]> {
  const posts = await import.meta.glob('../pages/blog/*.md');
  const postPromises = Object.entries(posts).map(async ([path, resolver]) => {
    const post = await resolver();
    const slug = path.split('/').pop()?.replace('.md', '') || '';
    
    return {
      slug,
      ...post.frontmatter,
      publishDate: new Date(post.frontmatter.publishDate)
    } as BlogPost;
  });
  
  const allPosts = await Promise.all(postPromises);
  
  return allPosts
    .filter(post => !post.draft)
    .sort((a, b) => b.publishDate.getTime() - a.publishDate.getTime());
}

export function getPostsByTag(posts: BlogPost[], tag: string): BlogPost[] {
  return posts.filter(post => post.tags.includes(tag));
}

export function getRecentPosts(posts: BlogPost[], limit: number = 5): BlogPost[] {
  return posts.slice(0, limit);
}

4. 性能优化实现

---
// OptimizedImage.astro - 图片优化组件
export interface Props {
  src: string;
  alt: string;
  width?: number;
  height?: number;
  loading?: 'lazy' | 'eager';
  class?: string;
}

const { 
  src, 
  alt, 
  width, 
  height, 
  loading = 'lazy', 
  class: className 
} = Astro.props;

// 生成响应式图片URL
const generateSrcSet = (baseSrc: string) => {
  const sizes = [320, 640, 1024, 1280];
  return sizes.map(size => 
    `${baseSrc}?w=${size}&q=80 ${size}w`
  ).join(', ');
};

const srcSet = generateSrcSet(src);
---

<picture class={className}>
  <source 
    srcset={srcSet}
    sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
  />
  <img 
    src={src}
    alt={alt}
    width={width}
    height={height}
    loading={loading}
    decoding="async"
  />
</picture>

<style>
  picture {
    display: block;
  }
  
  img {
    width: 100%;
    height: auto;
    border-radius: 8px;
  }
</style>

开发工具配置

1. TypeScript配置

// tsconfig.json
{
  "extends": "astro/tsconfigs/strict",
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@/components/*": ["src/components/*"],
      "@/layouts/*": ["src/layouts/*"],
      "@/utils/*": ["src/utils/*"]
    },
    "strict": true,
    "noEmit": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

2. Astro配置优化

// astro.config.mjs
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import tailwind from '@astrojs/tailwind';
import sitemap from '@astrojs/sitemap';

export default defineConfig({
  site: 'https://astro-new.example.com',
  integrations: [
    react(),
    tailwind(),
    sitemap()
  ],
  
  // 构建优化
  build: {
    inlineStylesheets: 'auto',
    split: true
  },
  
  // 开发服务器配置
  server: {
    port: 3000,
    host: true
  },
  
  // 实验性功能
  experimental: {
    assets: true,
    viewTransitions: true
  },
  
  // Vite配置
  vite: {
    optimizeDeps: {
      include: ['react', 'react-dom']
    },
    build: {
      rollupOptions: {
        output: {
          manualChunks: {
            react: ['react', 'react-dom']
          }
        }
      }
    }
  }
});

3. 样式系统

/* src/styles/global.css */
:root {
  /* 颜色系统 */
  --color-primary: #2563eb;
  --color-primary-dark: #1d4ed8;
  --color-secondary: #64748b;
  --color-accent: #f59e0b;
  
  /* 字体系统 */
  --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-mono: 'JetBrains Mono', 'Fira Code', monospace;
  
  /* 间距系统 */
  --space-xs: 0.25rem;
  --space-sm: 0.5rem;
  --space-md: 1rem;
  --space-lg: 2rem;
  --space-xl: 4rem;
  
  /* 断点系统 */
  --breakpoint-sm: 640px;
  --breakpoint-md: 768px;
  --breakpoint-lg: 1024px;
  --breakpoint-xl: 1280px;
}

/* 基础重置 */
*,
*::before,
*::after {
  box-sizing: border-box;
}

body {
  font-family: var(--font-sans);
  line-height: 1.6;
  color: #1f2937;
  background: #f8fafc;
}

/* 工具类 */
.container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 var(--space-md);
}

.grid {
  display: grid;
  gap: var(--space-lg);
}

.grid-2 {
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}

.grid-3 {
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}

/* 响应式工具 */
@media (max-width: 768px) {
  .container {
    padding: 0 var(--space-sm);
  }
  
  .grid {
    gap: var(--space-md);
  }
}

部署和优化

1. 构建优化

// 构建脚本优化
const buildConfig = {
  // 代码分割
  experimental: {
    integrations: true
  },
  
  // 资源优化
  vite: {
    build: {
      cssCodeSplit: true,
      sourcemap: false,
      minify: 'terser',
      terserOptions: {
        compress: {
          drop_console: true,
          drop_debugger: true
        }
      }
    }
  }
};

2. SEO优化

---
// SEOHead.astro - SEO组件
export interface Props {
  title: string;
  description: string;
  image?: string;
  canonical?: string;
  type?: 'website' | 'article';
}

const { 
  title, 
  description, 
  image = '/default-og.jpg',
  canonical = Astro.url.href,
  type = 'website'
} = Astro.props;

const fullTitle = `${title} | Astro New`;
---

<head>
  <!-- 基础SEO -->
  <title>{fullTitle}</title>
  <meta name="description" content={description} />
  <link rel="canonical" href={canonical} />
  
  <!-- Open Graph -->
  <meta property="og:type" content={type} />
  <meta property="og:title" content={fullTitle} />
  <meta property="og:description" content={description} />
  <meta property="og:image" content={image} />
  <meta property="og:url" content={canonical} />
  
  <!-- Twitter Card -->
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:title" content={fullTitle} />
  <meta name="twitter:description" content={description} />
  <meta name="twitter:image" content={image} />
  
  <!-- 技术标签 -->
  <meta name="generator" content={Astro.generator} />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>

性能指标

📊 Lighthouse评分

  • Performance: 98/100
  • Accessibility: 95/100
  • Best Practices: 100/100
  • SEO: 100/100

⚡ 加载性能

  • First Contentful Paint: < 1.2s
  • Largest Contentful Paint: < 2.5s
  • Cumulative Layout Shift: < 0.1
  • Time to Interactive: < 3.0s

学习成果

技术掌握

  1. Astro框架深度使用

    • 组件岛架构理解
    • 多框架集成实践
    • 构建优化配置
  2. 现代前端开发

    • TypeScript类型系统
    • CSS现代特性
    • 性能优化技巧
  3. 工程化实践

    • 项目结构设计
    • 开发工具配置
    • 部署流程优化

最佳实践总结

  • 组件设计原则
  • 性能优化策略
  • SEO优化方法
  • 可维护性考虑

未来规划

功能扩展

  • 添加国际化支持
  • 集成CMS系统
  • 实现主题切换
  • 添加搜索功能

技术升级

  • 升级到Astro最新版本
  • 集成更多UI框架
  • 优化构建性能
  • 增强开发体验

这个项目让我深入理解了现代静态站点生成的核心概念,掌握了Astro框架的精髓,为构建高性能Web应用奠定了坚实基础。