Migrate WordPress → Astro
Migrate WordPress → Astro
Section titled “Migrate WordPress → Astro”Chuyển từ WordPress (PHP + MySQL) sang Astro (Static HTML) mang lại hiệu suất vượt trội, bảo mật cao hơn, và chi phí hosting thấp hơn. Đây là hướng dẫn chi tiết cho quá trình migration.
Tại sao rờI WordPress?
Section titled “Tại sao rờI WordPress?”Phase 1: Export dữ liệu WordPress
Section titled “Phase 1: Export dữ liệu WordPress”Xuất Content
Section titled “Xuất Content”Cách 1: WordPress Export Tool
WordPress Admin → Tools → Export → All content→ Tải về file XMLCách 2: SQL Export (cho database lớn)
# Truy cập phpMyAdmin hoặc dùng WP-CLIwp db export backup.sql
# Hoặc export chỉ postswp db export posts.sql --tables=wp_postsCách 3: REST API
# Lấy tất cả posts qua APIcurl https://your-site.com/wp-json/wp/v2/posts?per_page=100 > posts.json
# Lấy pagescurl https://your-site.com/wp-json/wp/v2/pages > pages.jsonPhase 2: Convert Content
Section titled “Phase 2: Convert Content”XML → Markdown
Section titled “XML → Markdown”Dùng tool wordpress-export-to-markdown:
# Install toolnpm install -g wordpress-export-to-markdown
# Convertwordpress-export-to-markdown \ --input=export.xml \ --output=content \ --frontmatterHoặc dùng Python script:
import xml.etree.ElementTree as ETimport frontmatterimport os
def convert_wp_to_md(xml_file): tree = ET.parse(xml_file) root = tree.getroot()
# Namespace ns = {'content': 'http://purl.org/rss/1.0/modules/content/'}
for item in root.findall('.//item'): title = item.find('title').text content = item.find('content:encoded', ns).text
# Create markdown with frontmatter post = frontmatter.Post(content) post['title'] = title
# Save filename = f"{title.lower().replace(' ', '-')}.md" with open(f'content/{filename}', 'w') as f: f.write(frontmatter.dumps(post))
convert_wp_to_md('export.xml')Shortcodes → Components
Section titled “Shortcodes → Components”WordPress shortcodes cần chuyển thành Astro components:
WordPress:
[gallery ids="1,2,3"][button url="/contact" color="primary"]Contact Us[/button]Astro Components:
---const { ids } = Astro.props;const imageIds = ids.split(',');---
<div class="gallery"> {imageIds.map(id => ( <img src={`/images/${id}.jpg`} /> ))}</div>---const { url, variant = 'primary' } = Astro.props;---
<a href={url} class={`btn btn-${variant}`}> <slot /></a>Usage in Markdown:
import Gallery from '../components/Gallery.astro';import Button from '../components/Button.astro';
<Gallery ids="1,2,3" />
<Button url="/contact" variant="primary">Contact Us</Button>Phase 3: Setup Astro Project
Section titled “Phase 3: Setup Astro Project”Khởi tạo với Starlight (cho content-heavy sites)
Section titled “Khởi tạo với Starlight (cho content-heavy sites)”npm create astro@latest -- --template starlight
# Hoặc tự setupnpm create astro@latest wp-migrationcd wp-migrationnpm install @astrojs/starlightCấu trúc thư mục
Section titled “Cấu trúc thư mục”my-astro-site/├── src/│ ├── content/│ │ ├── blog/ # Posts từ WordPress│ │ │ ├── post-1.md│ │ │ └── post-2.md│ │ └── pages/ # Pages từ WordPress│ │ └── about.md│ ├── components/│ │ ├── Gallery.astro│ │ ├── Button.astro│ │ └── WpContent.astro│ └── layouts/│ └── PostLayout.astro├── public/│ └── uploads/ # Media từ WordPress└── astro.config.mjsPhase 4: Migrate Posts
Section titled “Phase 4: Migrate Posts”Content Collection
Section titled “Content Collection”src/content/config.ts:
import { defineCollection, z } from 'astro:content';
const blogCollection = defineCollection({ type: 'content', schema: z.object({ title: z.string(), date: z.date(), author: z.string().default('Admin'), categories: z.array(z.string()).default([]), tags: z.array(z.string()).default([]), featuredImage: z.string().optional(), excerpt: z.string().optional(), }),});
export const collections = { 'blog': blogCollection,};Post frontmatter mẫu
Section titled “Post frontmatter mẫu”---title: "Bài viết mẫu"date: 2024-01-15T10:00:00Zauthor: "Admin"categories: ["Tutorial", "Web Dev"]tags: ["astro", "wordpress"]featuredImage: "/uploads/2024/01/image.jpg"excerpt: "Tóm tắt bài viết"---
Nội dung bài viết ở đây...Dynamic routing cho posts
Section titled “Dynamic routing cho posts”src/pages/blog/[…slug].astro:
---import { getCollection } from 'astro:content';import PostLayout from '../../layouts/PostLayout.astro';
export async function getStaticPaths() { const posts = await getCollection('blog');
return posts.map(post => ({ params: { slug: post.slug }, props: { post }, }));}
const { post } = Astro.props;const { Content } = await post.render();---
<PostLayout frontmatter={post.data}> <Content /></PostLayout>Phase 5: Migrate Theme
Section titled “Phase 5: Migrate Theme”Convert WordPress theme → Astro
Section titled “Convert WordPress theme → Astro”WordPress (header.php):
<!DOCTYPE html><html <?php language_attributes(); ?>><head> <meta charset="<?php bloginfo('charset'); ?>"> <title><?php wp_title(); ?></title> <?php wp_head(); ?></head>Astro (Layout.astro):
---const { title, description } = Astro.props;---
<!DOCTYPE html><html lang="vi"><head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width" /> <title>{title}</title> <meta name="description" content={description} />
<link rel="stylesheet" href="/styles/global.css" /></head><body> <Header /> <slot /> <Footer /></body></html>Chuyển đổi CSS
Section titled “Chuyển đổi CSS”WordPress CSS thường có prefix .wp- hoặc dùng IDs. Clean up:
/* WordPress original */#primary.content-area { float: left; width: 70%;}
/* Astro version */.content-area { width: 70%;}
@media (max-width: 768px) { .content-area { width: 100%; }}Phase 6: Redirects
Section titled “Phase 6: Redirects”WordPress URLs → Astro URLs
Section titled “WordPress URLs → Astro URLs”WordPress dùng permalink structures khác nhau:
/?p=123(default)/2024/01/15/post-name/(date-based)/category/post-name/(category-based)
astro.config.mjs:
export default defineConfig({ redirects: { // Old WP URLs → New Astro URLs '/?p=123': '/blog/bai-viet-moi', '/2024/01/15/hello-world': '/blog/hello-world', '/category/news/page/2': '/blog/page/2', }});Hoặc dùng _redirects cho Netlify/Vercel:
/?p=123 /blog/bai-viet-moi 301/2024/* /blog/:splat 301Phase 7: Forms và Dynamic Features
Section titled “Phase 7: Forms và Dynamic Features”Contact Forms
Section titled “Contact Forms”Thay Contact Form 7 bằng:
- Netlify Forms:
<form name="contact" netlify> <input type="text" name="name" /> <input type="email" name="email" /> <textarea name="message"></textarea> <button type="submit">Send</button></form>- Formspree:
<form action="https://formspree.io/f/YOUR_ID" method="POST"> <!-- form fields --></form>- Astro API Routes:
export async function POST({ request }) { const data = await request.formData(); // Send email via SendGrid/Nodemailer return new Response('OK');}Comments
Section titled “Comments”Thay WordPress comments bằng:
- Disqus:
<script src="//site.disqus.com/embed.js"></script> - Giscus (GitHub Discussions): Free, open source
- Utterances: GitHub issues-based
Phase 8: SEO Migration
Section titled “Phase 8: SEO Migration”Giữ nguyên SEO value
Section titled “Giữ nguyên SEO value”Astro Sitemap
Section titled “Astro Sitemap”npm install @astrojs/sitemapimport sitemap from '@astrojs/sitemap';
export default defineConfig({ site: 'https://yoursite.com', integrations: [sitemap()],});RSS Feed
Section titled “RSS Feed”import rss from '@astrojs/rss';import { getCollection } from 'astro:content';
export async function GET(context) { const posts = await getCollection('blog');
return rss({ title: 'My Blog', description: 'Description', site: context.site, items: posts.map(post => ({ title: post.data.title, pubDate: post.data.date, description: post.data.excerpt, link: `/blog/${post.slug}/`, })), });}Phase 9: Deployment
Section titled “Phase 9: Deployment”Build static site
Section titled “Build static site”npm run build
# Output trong thư mục dist/Deploy options
Section titled “Deploy options”1. Vercel (Recommended)
npm i -g vercelvercel --prod2. Netlify
npm i -g netlify-clinetlify deploy --prod --dir=dist3. Cloudflare Pages
- Connect GitHub repo
- Build command:
npm run build - Output directory:
dist
4. GitHub Pages
name: Deployon: [push]jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: withastro/action@v1Post-Migration
Section titled “Post-Migration”Testing
Section titled “Testing”- Kiểm tra tất cả internal links
- Test forms hoạt động
- Verify images load đúng
- Check mobile responsive
- Run Lighthouse audit (target: 90+)
Monitor
Section titled “Monitor”- Google Search Console: Submit new sitemap
- Check for 404 errors
- Monitor rankings
Cleanup
Section titled “Cleanup”- Tắt WordPress hosting (sau 1-2 tháng ổn định)
- Backup WordPress files
- Cancel WP plugins/services