Clone Website Svelte → Astro
Clone Website Svelte → Astro
Section titled “Clone Website Svelte → Astro”Chuyển đổi từ SvelteKit sang Astro mang lại nhiều lợi ích: static site generation tốt hơn, SEO improved, và vẫn có thể dùng Svelte components. Bài viết này hướng dẫn quá trình migration.
So sánh SvelteKit vs Astro
Section titled “So sánh SvelteKit vs Astro”| Feature | SvelteKit | Astro |
|---|---|---|
| Rendering | SSR/CSR/SSG | SSG-first (SSR optional) |
| Hydration | Full app | Partial (islands) |
| Components | Svelte only | Multi-framework |
| Routing | File-based | File-based |
| Best for | Apps | Content sites |
Phase 1: Chuẩn bị
Section titled “Phase 1: Chuẩn bị”Phân tích SvelteKit project
Section titled “Phân tích SvelteKit project”# Xem cấu trúc project gốcfind . -type f -name "*.svelte" | head -20find . -type f -name "*.js" | grep -E "(store|util)"Khởi tạo Astro project
Section titled “Khởi tạo Astro project”npm create astro@latest migrated-site
# Chọn options:# - Empty project (tự migrate)# - Hoặc template phù hợp nhấtCài đặt Svelte integration:
cd migrated-sitenpm install svelte @astrojs/svelteUpdate astro.config.mjs:
import { defineConfig } from 'astro/config';import svelte from '@astrojs/svelte';
export default defineConfig({ integrations: [svelte()],});Phase 2: Migrate Routes
Section titled “Phase 2: Migrate Routes”SvelteKit → Astro Routing
Section titled “SvelteKit → Astro Routing”SvelteKit:
src/routes/├── +page.svelte # / (home)├── +layout.svelte # Root layout├── about/│ └── +page.svelte # /about└── blog/ ├── +page.svelte # /blog (list) └── [slug]/ └── +page.svelte # /blog/:slugAstro:
src/pages/├── index.astro # / (home)├── about.astro # /about└── blog/ ├── index.astro # /blog (list) └── [slug].astro # /blog/:slugVí dụ: Convert Homepage
Section titled “Ví dụ: Convert Homepage”SvelteKit (src/routes/+page.svelte):
<script> import Hero from '$lib/components/Hero.svelte'; import Features from '$lib/components/Features.svelte';
export let data; const { posts } = data;</script>
<Hero /><Features />
<ul> {#each posts as post} <li><a href="/blog/{post.slug}">{post.title}</a></li> {/each}</ul>Astro (src/pages/index.astro):
---import Hero from '../components/Hero.svelte';import Features from '../components/Features.svelte';
// Fetch data ở frontmatterconst posts = await fetch('https://api.example.com/posts') .then(r => r.json());---
<!-- Svelte components cần client directive --><Hero client:load /><Features client:load />
<ul> {posts.map(post => ( <li><a href={`/blog/${post.slug}`}>{post.title}</a></li> ))}</ul>Phase 3: Migrate Components
Section titled “Phase 3: Migrate Components”Svelte components hoạt động trong Astro
Section titled “Svelte components hoạt động trong Astro”Hầu hết Svelte components có thể dùng trực tiếp:
---import Counter from '../components/Counter.svelte';---
<!-- Interactive component --><Counter client:load />
<!-- Static component (không cần JS) --><StaticCard />Chuyển đổi Slot → Astro Slots
Section titled “Chuyển đổi Slot → Astro Slots”SvelteKit:
<div class="layout"> <Header /> <main> <slot /> <!-- Default slot --> </main> <Footer /></div>Astro:
---import Header from './Header.svelte';import Footer from './Footer.svelte';---
<div class="layout"> <Header /> <main> <slot /> <!-- Default slot --> </main> <Footer /></div>Chuyển đổi Props
Section titled “Chuyển đổi Props”SvelteKit:
<script> export let title; export let variant = 'primary';</script>Astro:
---const { title, variant = 'primary' } = Astro.props;---Phase 4: Migrate Data Fetching
Section titled “Phase 4: Migrate Data Fetching”Load functions → Frontmatter
Section titled “Load functions → Frontmatter”SvelteKit (+page.server.js):
export async function load() { const response = await fetch('https://api.example.com/posts'); const posts = await response.json();
return { posts };}Astro (.astro file):
---const response = await fetch('https://api.example.com/posts');const posts = await response.json();---
<ul> {posts.map(post => <li>{post.title}</li>)}</ul>Stores → Nano Stores
Section titled “Stores → Nano Stores”SvelteKit (stores.js):
import { writable } from 'svelte/store';
export const count = writable(0);Astro (stores.js):
import { atom } from 'nanostores';
export const count = atom(0);Trong Svelte component:
<script> import { count } from '../stores'; import { useStore } from '@nanostores/react'; // hoặc adapter phù hợp</script>Phase 5: CSS Migration
Section titled “Phase 5: CSS Migration”Svelte scoped CSS → Astro scoped CSS
Section titled “Svelte scoped CSS → Astro scoped CSS”Svelte:
<style> /* Tự động scoped */ .card { background: white; }</style>Astro:
<style> /* Cũng tự động scoped */ .card { background: white; }</style>CSS Variables
Section titled “CSS Variables”Chuyển đổi trực tiếp:
/* Cả hai framework đều hỗ trợ CSS variables */:root { --color-primary: #e11d48; --font-sans: 'Inter', sans-serif;}Phase 6: Form Handling
Section titled “Phase 6: Form Handling”SvelteKit form actions
Section titled “SvelteKit form actions”SvelteKit:
<form method="POST" action="?/create"> <input name="title" /> <button type="submit">Create</button></form>Astro:
---if (Astro.request.method === 'POST') { const formData = await Astro.request.formData(); const title = formData.get('title'); // Handle submission}---
<form method="POST"> <input name="title" /> <button type="submit">Create</button></form>Phase 7: Testing
Section titled “Phase 7: Testing”Build và Preview
Section titled “Build và Preview”# Build static sitenpm run build
# Preview production buildnpm run previewChecklist
Section titled “Checklist”- Tất cả routes hoạt động
- Svelte components render đúng
- Data fetching hoạt động
- Forms submit đúng
- CSS giống site gốc
- Responsive hoạt động
- Không có console errors
Troubleshooting
Section titled “Troubleshooting”Lỗi 1: “window is not defined”
Section titled “Lỗi 1: “window is not defined””Nguyên nhân: Code chạy server-side
Fix:
<script> import { onMount } from 'svelte';
onMount(() => { // Code dùng window ở đây console.log(window.location); });</script>Lỗi 2: Store không hoạt động
Section titled “Lỗi 2: Store không hoạt động”Fix: Chuyển sang Nano Stores
Lỗi 3: Hydration mismatch
Section titled “Lỗi 3: Hydration mismatch”Fix: Thêm client:only cho component phức tạp
<ComplexComponent client:only="svelte" />