Skip to content

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.

FeatureSvelteKitAstro
RenderingSSR/CSR/SSGSSG-first (SSR optional)
HydrationFull appPartial (islands)
ComponentsSvelte onlyMulti-framework
RoutingFile-basedFile-based
Best forAppsContent sites
Terminal window
# Xem cấu trúc project gốc
find . -type f -name "*.svelte" | head -20
find . -type f -name "*.js" | grep -E "(store|util)"
Terminal window
npm create astro@latest migrated-site
# Chọn options:
# - Empty project (tự migrate)
# - Hoặc template phù hợp nhất

Cài đặt Svelte integration:

Terminal window
cd migrated-site
npm install svelte @astrojs/svelte

Update astro.config.mjs:

import { defineConfig } from 'astro/config';
import svelte from '@astrojs/svelte';
export default defineConfig({
integrations: [svelte()],
});

SvelteKit:

src/routes/
├── +page.svelte # / (home)
├── +layout.svelte # Root layout
├── about/
│ └── +page.svelte # /about
└── blog/
├── +page.svelte # /blog (list)
└── [slug]/
└── +page.svelte # /blog/:slug

Astro:

src/pages/
├── index.astro # / (home)
├── about.astro # /about
└── blog/
├── index.astro # /blog (list)
└── [slug].astro # /blog/:slug

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 ở frontmatter
const 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>

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:

src/pages/index.astro
---
import Counter from '../components/Counter.svelte';
---
<!-- Interactive component -->
<Counter client:load />
<!-- Static component (không cần JS) -->
<StaticCard />

SvelteKit:

Layout.svelte
<div class="layout">
<Header />
<main>
<slot /> <!-- Default slot -->
</main>
<Footer />
</div>

Astro:

Layout.astro
---
import Header from './Header.svelte';
import Footer from './Footer.svelte';
---
<div class="layout">
<Header />
<main>
<slot /> <!-- Default slot -->
</main>
<Footer />
</div>

SvelteKit:

<script>
export let title;
export let variant = 'primary';
</script>

Astro:

---
const { title, variant = 'primary' } = Astro.props;
---

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>

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>

Svelte:

<style>
/* Tự động scoped */
.card { background: white; }
</style>

Astro:

<style>
/* Cũng tự động scoped */
.card { background: white; }
</style>

Chuyển đổi trực tiếp:

/* Cả hai framework đều hỗ trợ CSS variables */
:root {
--color-primary: #e11d48;
--font-sans: 'Inter', sans-serif;
}

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>
Terminal window
# Build static site
npm run build
# Preview production build
npm run preview
  • 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

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>

Fix: Chuyển sang Nano Stores

Fix: Thêm client:only cho component phức tạp

<ComplexComponent client:only="svelte" />