React × Next.js – Trái tim UI Tử Vi hiện đại
Stack: Next 13 (App Router) · React 18 · Tailwind CSS · SWR · Chart.js
Backend: Golang API (gRPC + REST) · Postgres · Redis
Deploy: Vercel Edge Functions + AWS ECS (Go)
Trong dự án AstroCore (nền tảng Tử Vi đã giới thiệu ở bài trước), React/Next đóng vai trò “cửa sổ” giúp người dùng tương tác trực quan với những thuật toán Tử Vi phức tạp. Bài viết đào sâu mọi lớp:
- Kiến trúc App Router & Server Components
- Luồng dữ liệu real-time giữa Go API ↔ React
- Hiển thị lá số, cung, sao bằng Chart.js & SVG
- State Management “không Redux” với Zustand + SWR
- SEO & i18n cho từ khóa “tu vi” (~1,2 triệu tìm kiếm/tháng)
- Đo lường & tối ưu Web Vitals dưới 1 s LCP
1. Tại sao Next 13 App Router?
Yêu cầu | Tính năng App Router đáp ứng |
---|---|
SEO trang /cung/menh tĩnh | Static Generation (export const dynamic = 'force-static' ) |
Hồ sơ lá số cá nhân (auth cookie) | Server Components + cookies() |
Bản dùng thử iframe trên blog | Edge Runtime (export const runtime = 'edge' ) |
Chặn crawl PDF premium | robots.txt middleware |
Server Actions cho phép gọi API Go trực tiếp trên máy chủ mà không lộ token:
'use server'
export async function fetchHoroscope(dob: string, gender: 'M'|'F') {
const res = await fetch(`${process.env.API_URL}/horoscope`, {
method: 'POST',
body: JSON.stringify({ dob, gender })
})
return res.json()
}
2. Component hóa lá số Tử Vi
``` Horoscopes/ ├── ChartCanvas.tsx ├── PalaceGrid.tsx ├── StarTooltip.tsx └── HoroscopeCard.tsx ```
PalaceGrid.tsx (trích đoạn)
import { motion } from 'framer-motion'
export const PalaceGrid = ({ palaces }) => (
<svg viewBox="0 0 300 300" className="w-full h-auto">
{palaces.map((p,i)=>(
<motion.g key={i} initial={{opacity:0,scale:.8}} animate={{opacity:1,scale:1}}>
<rect x={p.x} y={p.y} width="90" height="90" rx="8"
className="fill-slate-50 stroke-slate-300 dark:fill-slate-800"/>
<text x={p.x+45} y={p.y+12} textAnchor="middle"
className="font-semibold text-xs">{p.name}</text>
</motion.g>
))}
</svg>
)
3. SWR + Zustand gọn nhẹ
import useSWR from 'swr'
import { create } from 'zustand'
export const useUserStore = create(()=>({ dob:'', gender:'M' }))
export const useHoroscope = () =>{
const { dob, gender } = useUserStore()
return useSWR(dob ? ['horoscope', dob, gender] : null,
()=>fetchHoroscope(dob, gender),
{ suspense:true })
}
4. Chart.js vẽ đại vận
import { Line } from 'react-chartjs-2'
export const LuckCycle = ({ data })=>(
<Line data={{ labels:data.years, datasets:[{ data:data.score, tension:.4 }]}}
options={{ plugins:{ legend:{display:false}} }}/>
)
5. SEO & i18n
export const generateStaticParams = ()=>[{lang:'vi'},{lang:'en'}]
export const metadata = { title:{default:'Tử Vi – AstroCore'} }
6. Edge Function demo
export const runtime='edge'
export async function GET(req:Request){
const url=new URL(req.url)
const dob=url.searchParams.get('dob')!
const data=await fetch(`${API}/horoscope?dob=${dob}`).then(r=>r.json())
return Response.json(data,{headers:{'Cache-Control':'s-maxage=86400'}})
}
7. Kết quả Web Vitals
Metric | CSR | RSC |
---|---|---|
LCP | 2.8 s | 0.94 s |
JS | 430 kB | 180 kB |
8. Gotcha & mẹo
- Chart.js SSR → import trong client component.
- Mismatch SVG → wrap số động trong
useEffect
.
React + Next 13 đã giúp app Tử Vi đạt SEO, hiệu năng & UX vượt trội, đồng thời sẵn sàng scale đa ngôn ngữ và widget B2B.