經過 Day 21-23 的測試三部曲,我們已經建立了完整的測試生態系統。今天我們開啟新的篇章:打造一個專業的 Landing Page,作為 Kyo System 企業級微服務生態系的門面。我們將深入探討現代 Landing Page 的設計原則、動畫效果、效能優化,以及轉換率優化策略。
一個成功的 SaaS Landing Page 需要具備以下要素:
/**
* Landing Page 成功要素分析
*
* 1. Hero Section (主頁橫幅)
* ✅ 3 秒內傳達核心價值
* ✅ 清晰的 CTA (Call-to-Action)
* ✅ 視覺吸引力(動畫、圖片)
*
* 2. Value Proposition (價值主張)
* ✅ 解決什麼問題
* ✅ 為什麼選擇我們
* ✅ 量化成果(數據支持)
*
* 3. Features (功能展示)
* ✅ 3-6 個核心功能
* ✅ 視覺化呈現
* ✅ 清晰的分類
*
* 4. Social Proof (社交證明)
* ✅ 客戶評價
* ✅ 使用數據
* ✅ 合作夥伴 Logo
*
* 5. Pricing (定價)
* ✅ 清晰透明
* ✅ 多層級方案
* ✅ 突出推薦方案
*
* 6. FAQ (常見問題)
* ✅ 預先回答疑慮
* ✅ 降低轉換障礙
*
* 7. Final CTA (最後行動呼籲)
* ✅ 強化訊息
* ✅ 降低風險(免費試用)
*
* 轉換率基準:
* - 優秀: 5%+
* - 良好: 2-5%
* - 一般: 1-2%
* - 需改進: <1%
*/
// src/pages/Landing/types.ts
/**
* Landing Page 技術棧
*
* UI 框架: React 18 + TypeScript
* 樣式方案: Mantine UI + Tailwind CSS (optional)
* 動畫庫: Framer Motion
* Icons: Tabler Icons (Mantine 內建)
* 表單處理: React Hook Form + Zod
* SEO: React Helmet Async
* Analytics: Google Analytics 4 / PostHog
*/
export interface LandingPageSection {
id: string;
title: string;
visible: boolean;
}
export interface CTAButton {
text: string;
variant: 'primary' | 'secondary';
action: () => void;
tracking?: {
event: string;
category: string;
};
}
export interface Feature {
id: string;
icon: React.ComponentType<{ size?: number }>;
title: string;
description: string;
details?: string[];
}
export interface Testimonial {
id: string;
name: string;
role: string;
company: string;
avatar?: string;
content: string;
rating: number;
}
export interface PricingPlan {
id: string;
name: string;
price: number;
currency: string;
interval: 'month' | 'year';
description: string;
features: string[];
highlighted?: boolean;
ctaText: string;
ctaAction: () => void;
}
// src/pages/Landing/sections/HeroSection.tsx
import { Container, Title, Text, Button, Group, Box, Image } from '@mantine/core';
import { motion } from 'framer-motion';
import { IconArrowRight, IconBrandGithub } from '@tabler/icons-react';
import { useNavigate } from 'react-router-dom';
const MotionBox = motion(Box);
const MotionTitle = motion(Title);
const MotionText = motion(Text);
export function HeroSection() {
const navigate = useNavigate();
// 動畫變體
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.2,
},
},
};
const itemVariants = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.6,
ease: [0.6, -0.05, 0.01, 0.99],
},
},
};
const floatingVariants = {
initial: { y: 0 },
animate: {
y: [-10, 10, -10],
transition: {
duration: 6,
repeat: Infinity,
ease: 'easeInOut',
},
},
};
return (
<Box
component="section"
sx={(theme) => ({
position: 'relative',
minHeight: 'calc(100vh - 60px)',
display: 'flex',
alignItems: 'center',
background: `linear-gradient(135deg, ${theme.colors.violet[9]} 0%, ${theme.colors.blue[9]} 100%)`,
overflow: 'hidden',
// 背景裝飾
'&::before': {
content: '""',
position: 'absolute',
top: '-50%',
right: '-20%',
width: '800px',
height: '800px',
borderRadius: '50%',
background: `radial-gradient(circle, ${theme.fn.rgba(theme.white, 0.1)} 0%, transparent 70%)`,
pointerEvents: 'none',
},
'&::after': {
content: '""',
position: 'absolute',
bottom: '-30%',
left: '-10%',
width: '600px',
height: '600px',
borderRadius: '50%',
background: `radial-gradient(circle, ${theme.fn.rgba(theme.white, 0.08)} 0%, transparent 70%)`,
pointerEvents: 'none',
},
})}
>
<Container size="xl" sx={{ position: 'relative', zIndex: 1 }}>
<MotionBox
variants={containerVariants}
initial="hidden"
animate="visible"
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
gap: '3rem',
alignItems: 'center',
}}
>
{/* 左側文字內容 */}
<Box>
<MotionTitle
variants={itemVariants}
order={1}
size="h1"
sx={(theme) => ({
color: theme.white,
fontSize: '3.5rem',
fontWeight: 800,
lineHeight: 1.2,
marginBottom: theme.spacing.md,
'@media (max-width: 768px)': {
fontSize: '2.5rem',
},
})}
>
從 OTP 驗證開始
<br />
打造你的 SaaS 基礎建設
</MotionTitle>
<MotionText
variants={itemVariants}
size="xl"
sx={(theme) => ({
color: theme.fn.rgba(theme.white, 0.9),
marginBottom: theme.spacing.xl,
lineHeight: 1.6,
})}
>
Kyo System 是一套模組化的企業級微服務生態系,提供開箱即用的 SaaS 基礎建設。
<br />
<strong>首發產品 Kyo OTP - 專業的驗證碼服務,讓你專注於核心業務</strong>
</MotionText>
{/* 關鍵數據展示 */}
<MotionBox variants={itemVariants}>
<Group spacing="xl" mb="xl">
<Box>
<Text size="xl" weight={700} sx={{ color: 'white' }}>
99.9%
</Text>
<Text size="sm" sx={(theme) => ({ color: theme.fn.rgba(theme.white, 0.7) })}>
系統可用性
</Text>
</Box>
<Box>
<Text size="xl" weight={700} sx={{ color: 'white' }}>
<100ms
</Text>
<Text size="sm" sx={(theme) => ({ color: theme.fn.rgba(theme.white, 0.7) })}>
平均回應時間
</Text>
</Box>
<Box>
<Text size="xl" weight={700} sx={{ color: 'white' }}>
1M+
</Text>
<Text size="sm" sx={(theme) => ({ color: theme.fn.rgba(theme.white, 0.7) })}>
每日發送量
</Text>
</Box>
</Group>
</MotionBox>
{/* CTA 按鈕 */}
<MotionBox variants={itemVariants}>
<Group spacing="md">
<Button
size="lg"
radius="md"
rightIcon={<IconArrowRight size={20} />}
sx={(theme) => ({
background: theme.white,
color: theme.colors.violet[9],
'&:hover': {
background: theme.fn.rgba(theme.white, 0.9),
transform: 'translateY(-2px)',
},
transition: 'all 0.3s ease',
boxShadow: '0 10px 30px rgba(0,0,0,0.2)',
})}
onClick={() => navigate('/signup')}
>
免費開始使用
</Button>
<Button
size="lg"
radius="md"
variant="outline"
leftIcon={<IconBrandGithub size={20} />}
sx={(theme) => ({
color: theme.white,
borderColor: theme.white,
'&:hover': {
background: theme.fn.rgba(theme.white, 0.1),
transform: 'translateY(-2px)',
},
transition: 'all 0.3s ease',
})}
onClick={() => window.open('https://github.com/kyong-saas', '_blank')}
>
查看文件
</Button>
</Group>
<Text
size="sm"
mt="md"
sx={(theme) => ({
color: theme.fn.rgba(theme.white, 0.6),
})}
>
✨ 無需信用卡 · 每月 1,000 則免費額度
</Text>
</MotionBox>
</Box>
{/* 右側視覺元素 */}
<MotionBox
variants={floatingVariants}
initial="initial"
animate="animate"
sx={{
position: 'relative',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
{/* 這裡可以放產品截圖、動畫示意圖等 */}
<Box
sx={(theme) => ({
width: '100%',
maxWidth: '500px',
aspectRatio: '4/3',
background: theme.fn.rgba(theme.white, 0.1),
borderRadius: theme.radius.lg,
border: `2px solid ${theme.fn.rgba(theme.white, 0.2)}`,
backdropFilter: 'blur(10px)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
boxShadow: '0 20px 60px rgba(0,0,0,0.3)',
})}
>
{/* 產品截圖或動畫放這裡 */}
<DashboardPreview />
</Box>
</MotionBox>
</MotionBox>
</Container>
{/* 向下滾動提示 */}
<MotionBox
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1.5 }}
sx={{
position: 'absolute',
bottom: '2rem',
left: '50%',
transform: 'translateX(-50%)',
}}
>
<motion.div
animate={{ y: [0, 10, 0] }}
transition={{ duration: 1.5, repeat: Infinity }}
>
<Text
size="sm"
sx={(theme) => ({
color: theme.fn.rgba(theme.white, 0.6),
textAlign: 'center',
})}
>
向下滾動了解更多
</Text>
</motion.div>
</MotionBox>
</Box>
);
}
/**
* Dashboard 預覽元件
*/
function DashboardPreview() {
return (
<Box p="xl" w="100%">
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ delay: 0.5, duration: 0.8 }}
>
<Box
sx={(theme) => ({
background: theme.white,
borderRadius: theme.radius.md,
padding: theme.spacing.md,
boxShadow: theme.shadows.xl,
})}
>
{/* 模擬儀表板介面 */}
<Group spacing="xs" mb="md">
<Box
sx={{ width: 12, height: 12, borderRadius: '50%', background: '#FF5F56' }}
/>
<Box
sx={{ width: 12, height: 12, borderRadius: '50%', background: '#FFBD2E' }}
/>
<Box
sx={{ width: 12, height: 12, borderRadius: '50%', background: '#27C93F' }}
/>
</Group>
<Box
sx={(theme) => ({
height: '300px',
background: theme.fn.gradient({ from: 'violet', to: 'blue', deg: 135 }),
borderRadius: theme.radius.md,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
})}
>
<Text size="lg" weight={700} sx={{ color: 'white' }}>
OTP Dashboard
</Text>
</Box>
</Box>
</motion.div>
</Box>
);
}
// src/pages/Landing/sections/FeaturesSection.tsx
import { Container, Title, Text, SimpleGrid, Box, ThemeIcon, Badge } from '@mantine/core';
import { motion, useInView } from 'framer-motion';
import { useRef } from 'react';
import {
IconShield,
IconBolt,
IconUsers,
IconChartLine,
IconCloud,
IconLock,
} from '@tabler/icons-react';
const MotionBox = motion(Box);
const features = [
{
icon: IconShield,
title: 'Kyo OTP - 驗證碼服務',
description: '首發產品,提供企業級的一次性密碼驗證服務',
details: ['簡訊/Email OTP', '多通道整合', '高送達率'],
color: 'violet',
},
{
icon: IconUsers,
title: '多租戶架構',
description: '完整的租戶隔離,支援 SaaS 多客戶場景',
details: ['資料隔離', '獨立配額', '租戶管理'],
color: 'cyan',
},
{
icon: IconCloud,
title: '微服務架構',
description: '模組化設計,每個服務可獨立使用或整合',
details: ['服務解耦', '獨立擴展', '容錯隔離'],
color: 'blue',
},
{
icon: IconChartLine,
title: '即時監控與分析',
description: '完整的可觀測性,掌握系統運行狀態',
details: ['即時儀表板', '效能指標', '告警通知'],
color: 'teal',
},
{
icon: IconBolt,
title: 'RESTful API 優先',
description: 'oRPC 標準化 API,快速整合到現有系統',
details: ['OpenAPI 規範', 'SDK 支援', 'Webhook 回調'],
color: 'indigo',
},
{
icon: IconLock,
title: 'AWS 雲端原生',
description: '99.9% SLA 保證,基於 AWS 最佳實踐',
details: ['自動擴展', '多區域部署', '災難恢復'],
color: 'grape',
},
];
export function FeaturesSection() {
const ref = useRef(null);
const isInView = useInView(ref, { once: true, margin: '-100px' });
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1,
},
},
};
const itemVariants = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.5,
},
},
};
return (
<Box
component="section"
ref={ref}
py={80}
sx={(theme) => ({
background: theme.colorScheme === 'dark' ? theme.colors.dark[8] : theme.white,
})}
>
<Container size="xl">
<MotionBox
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }}
transition={{ duration: 0.6 }}
mb={50}
sx={{ textAlign: 'center' }}
>
<Badge size="lg" variant="gradient" gradient={{ from: 'violet', to: 'blue' }} mb="md">
強大功能
</Badge>
<Title order={2} size="h1" mb="md">
為什麼選擇 Kyo System?
</Title>
<Text size="lg" color="dimmed" maw={600} mx="auto">
模組化的企業級微服務生態系,從 OTP 驗證開始,逐步建構完整的 SaaS 基礎建設
</Text>
</MotionBox>
<MotionBox
variants={containerVariants}
initial="hidden"
animate={isInView ? 'visible' : 'hidden'}
>
<SimpleGrid
cols={3}
spacing="xl"
breakpoints={[
{ maxWidth: 'md', cols: 2 },
{ maxWidth: 'sm', cols: 1 },
]}
>
{features.map((feature, index) => (
<FeatureCard key={index} {...feature} variants={itemVariants} />
))}
</SimpleGrid>
</MotionBox>
</Container>
</Box>
);
}
interface FeatureCardProps {
icon: React.ComponentType<{ size?: number }>;
title: string;
description: string;
details: string[];
color: string;
variants: any;
}
function FeatureCard({ icon: Icon, title, description, details, color, variants }: FeatureCardProps) {
return (
<MotionBox
variants={variants}
whileHover={{ y: -5 }}
sx={(theme) => ({
padding: theme.spacing.xl,
borderRadius: theme.radius.lg,
border: `1px solid ${
theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[2]
}`,
background: theme.colorScheme === 'dark' ? theme.colors.dark[7] : theme.white,
transition: 'all 0.3s ease',
cursor: 'pointer',
'&:hover': {
boxShadow: theme.shadows.lg,
borderColor: theme.colors[color][5],
},
})}
>
<ThemeIcon
size={60}
radius="md"
variant="gradient"
gradient={{ from: color, to: `${color}.7` }}
mb="md"
>
<Icon size={30} />
</ThemeIcon>
<Title order={3} size="h3" mb="sm">
{title}
</Title>
<Text color="dimmed" mb="md">
{description}
</Text>
<Box>
{details.map((detail, idx) => (
<Text
key={idx}
size="sm"
color="dimmed"
sx={(theme) => ({
display: 'flex',
alignItems: 'center',
marginBottom: theme.spacing.xs,
'&::before': {
content: '"✓"',
display: 'inline-block',
marginRight: theme.spacing.xs,
color: theme.colors[color][6],
fontWeight: 700,
},
})}
>
{detail}
</Text>
))}
</Box>
</MotionBox>
);
}
// src/pages/Landing/sections/PricingSection.tsx
import { Container, Title, Text, SimpleGrid, Box, Button, Badge, List } from '@mantine/core';
import { motion, useInView } from 'framer-motion';
import { useRef } from 'react';
import { IconCheck, IconArrowRight } from '@tabler/icons-react';
import { useNavigate } from 'react-router-dom';
const MotionBox = motion(Box);
const pricingPlans = [
{
name: 'Starter',
price: 0,
interval: 'month',
description: '適合個人開發者與小型專案',
features: [
'每月 1,000 則免費額度',
'基本 API 存取',
'郵件支援',
'7 天資料保留',
'基礎統計報表',
],
highlighted: false,
ctaText: '免費開始',
},
{
name: 'Professional',
price: 99,
interval: 'month',
description: '適合成長中的團隊與企業',
features: [
'每月 50,000 則額度',
'完整 API 功能',
'優先郵件與線上支援',
'30 天資料保留',
'進階分析與報表',
'自訂 Webhook',
'多租戶管理',
],
highlighted: true,
ctaText: '立即升級',
badge: '最受歡迎',
},
{
name: 'Enterprise',
price: null,
interval: 'month',
description: '適合大型企業與特殊需求',
features: [
'無限制發送量',
'專屬 API 端點',
'24/7 技術支援',
'無限資料保留',
'客製化報表',
'SLA 保證',
'專屬客戶經理',
'私有部署選項',
],
highlighted: false,
ctaText: '聯絡銷售',
},
];
export function PricingSection() {
const ref = useRef(null);
const isInView = useInView(ref, { once: true, margin: '-100px' });
const navigate = useNavigate();
return (
<Box
component="section"
ref={ref}
py={80}
sx={(theme) => ({
background: theme.fn.gradient({ from: 'violet.9', to: 'blue.9', deg: 135 }),
})}
>
<Container size="xl">
<MotionBox
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }}
transition={{ duration: 0.6 }}
mb={50}
sx={{ textAlign: 'center' }}
>
<Title order={2} size="h1" mb="md" sx={{ color: 'white' }}>
簡單透明的定價
</Title>
<Text size="lg" sx={(theme) => ({ color: theme.fn.rgba(theme.white, 0.8) })} maw={600} mx="auto">
選擇最適合你的方案,隨時可以升級或降級
</Text>
</MotionBox>
<SimpleGrid
cols={3}
spacing="xl"
breakpoints={[
{ maxWidth: 'md', cols: 2 },
{ maxWidth: 'sm', cols: 1 },
]}
>
{pricingPlans.map((plan, index) => (
<PricingCard
key={index}
{...plan}
onCTAClick={() => {
if (plan.price === null) {
window.location.href = 'mailto:sales@kyong.com';
} else {
navigate('/signup');
}
}}
delay={index * 0.1}
isInView={isInView}
/>
))}
</SimpleGrid>
<MotionBox
initial={{ opacity: 0 }}
animate={isInView ? { opacity: 1 } : { opacity: 0 }}
transition={{ delay: 0.6 }}
mt={50}
>
<Text size="sm" sx={(theme) => ({ color: theme.fn.rgba(theme.white, 0.6), textAlign: 'center' })}>
✨ 所有方案皆包含 SSL 加密、自動備份、以及基礎 DDoS 防護
</Text>
</MotionBox>
</Container>
</Box>
);
}
interface PricingCardProps {
name: string;
price: number | null;
interval: string;
description: string;
features: string[];
highlighted: boolean;
ctaText: string;
badge?: string;
onCTAClick: () => void;
delay: number;
isInView: boolean;
}
function PricingCard({
name,
price,
interval,
description,
features,
highlighted,
ctaText,
badge,
onCTAClick,
delay,
isInView,
}: PricingCardProps) {
return (
<MotionBox
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }}
transition={{ duration: 0.5, delay }}
whileHover={{ y: -10 }}
sx={(theme) => ({
padding: theme.spacing.xl,
borderRadius: theme.radius.lg,
background: theme.white,
boxShadow: highlighted ? '0 20px 60px rgba(0,0,0,0.3)' : theme.shadows.md,
border: highlighted ? `3px solid ${theme.colors.yellow[5]}` : 'none',
position: 'relative',
transition: 'all 0.3s ease',
transform: highlighted ? 'scale(1.05)' : 'scale(1)',
'&:hover': {
boxShadow: '0 20px 60px rgba(0,0,0,0.3)',
},
})}
>
{badge && (
<Badge
size="lg"
variant="gradient"
gradient={{ from: 'yellow', to: 'orange' }}
sx={{
position: 'absolute',
top: -12,
right: 20,
}}
>
{badge}
</Badge>
)}
<Title order={3} size="h3" mb="xs">
{name}
</Title>
<Text color="dimmed" size="sm" mb="xl">
{description}
</Text>
<Box mb="xl">
{price === null ? (
<Title order={2} size="h2">
聯絡我們
</Title>
) : (
<>
<Text
component="span"
sx={{ fontSize: '3rem', fontWeight: 700, lineHeight: 1 }}
>
${price}
</Text>
<Text component="span" color="dimmed" size="lg" ml="xs">
/ {interval === 'month' ? '月' : '年'}
</Text>
</>
)}
</Box>
<List
spacing="sm"
mb="xl"
icon={
<Box
sx={(theme) => ({
width: 20,
height: 20,
borderRadius: '50%',
background: theme.fn.gradient({ from: 'violet', to: 'blue' }),
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
})}
>
<IconCheck size={14} color="white" />
</Box>
}
>
{features.map((feature, idx) => (
<List.Item key={idx}>
<Text size="sm">{feature}</Text>
</List.Item>
))}
</List>
<Button
fullWidth
size="lg"
variant={highlighted ? 'gradient' : 'outline'}
gradient={highlighted ? { from: 'violet', to: 'blue' } : undefined}
rightIcon={<IconArrowRight size={18} />}
onClick={onCTAClick}
sx={(theme) => ({
'&:hover': {
transform: 'translateY(-2px)',
},
transition: 'all 0.3s ease',
})}
>
{ctaText}
</Button>
</MotionBox>
);
}
// src/pages/Landing/LandingPage.tsx
import { Helmet } from 'react-helmet-async';
import { HeroSection } from './sections/HeroSection';
import { FeaturesSection } from './sections/FeaturesSection';
import { PricingSection } from './sections/PricingSection';
import { TestimonialsSection } from './sections/TestimonialsSection';
import { FAQSection } from './sections/FAQSection';
import { CTASection } from './sections/CTASection';
export function LandingPage() {
// 結構化資料 (JSON-LD)
const structuredData = {
'@context': 'https://schema.org',
'@type': 'SoftwareApplication',
name: 'Kyo System',
applicationCategory: 'BusinessApplication',
description: '企業級微服務生態系,提供模組化的 SaaS 基礎建設',
offers: {
'@type': 'Offer',
price: '0',
priceCurrency: 'USD',
},
aggregateRating: {
'@type': 'AggregateRating',
ratingValue: '4.8',
ratingCount: '127',
},
};
return (
<>
<Helmet>
{/* 基本 SEO */}
<title>Kyo System - 企業級微服務生態系 | 從 OTP 驗證開始</title>
<meta
name="description"
content="Kyo System 提供模組化的企業級微服務生態系。首發產品 Kyo OTP - 專業驗證碼服務,支援多租戶、微服務架構、AWS 雲端原生。99.9% SLA 保證。"
/>
<meta
name="keywords"
content="Kyo System, 微服務, SaaS, OTP, 一次性密碼, 驗證碼, 多租戶, AWS, 雲端原生"
/>
{/* Open Graph */}
<meta property="og:title" content="Kyo System - 企業級微服務生態系 | 從 OTP 驗證開始" />
<meta
property="og:description"
content="模組化的企業級微服務生態系,首發產品 Kyo OTP 提供專業的驗證碼服務"
/>
<meta property="og:type" content="website" />
<meta property="og:url" content="https://kyong.com" />
<meta property="og:image" content="https://kyong.com/og-image.png" />
{/* Twitter Card */}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Kyo System - 企業級微服務生態系 | 從 OTP 驗證開始" />
<meta
name="twitter:description"
content="模組化的企業級微服務生態系,首發產品 Kyo OTP 提供專業的驗證碼服務"
/>
<meta name="twitter:image" content="https://kyong.com/twitter-image.png" />
{/* 結構化資料 */}
<script type="application/ld+json">{JSON.stringify(structuredData)}</script>
{/* Canonical URL */}
<link rel="canonical" href="https://kyong.com" />
</Helmet>
<main>
<HeroSection />
<FeaturesSection />
<PricingSection />
<TestimonialsSection />
<FAQSection />
<CTASection />
</main>
</>
);
}
// src/pages/Landing/optimizations.ts
/**
* Landing Page 效能優化策略
*
* 1. 程式碼分割 (Code Splitting)
* - 路由層級分割
* - 元件層級懶加載
* - 第三方庫按需載入
*
* 2. 圖片優化
* - WebP 格式
* - 響應式圖片 (srcset)
* - 懶加載 (Lazy Loading)
* - CDN 託管
*
* 3. 資源預載入
* - DNS Prefetch
* - Preconnect
* - Prefetch 關鍵資源
*
* 4. 快取策略
* - Service Worker
* - HTTP 快取頭
* - LocalStorage 快取
*
* 5. 關鍵渲染路徑優化
* - 內聯關鍵 CSS
* - 延遲載入非關鍵 JS
* - 減少 Render-blocking 資源
*
* 6. 字型優化
* - 字型子集化
* - font-display: swap
* - 預載入字型
*/
// 懶加載圖片
import { useEffect, useRef, useState } from 'react';
export function useLazyImage(src: string) {
const [imageSrc, setImageSrc] = useState<string | null>(null);
const imgRef = useRef<HTMLImageElement>(null);
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setImageSrc(src);
observer.disconnect();
}
});
},
{ rootMargin: '50px' }
);
if (imgRef.current) {
observer.observe(imgRef.current);
}
return () => observer.disconnect();
}, [src]);
return { imageSrc, imgRef };
}
// 預載入關鍵資源
export function preloadCriticalAssets() {
// 預載入字型
const fontLink = document.createElement('link');
fontLink.rel = 'preload';
fontLink.as = 'font';
fontLink.type = 'font/woff2';
fontLink.href = '/fonts/inter-var.woff2';
fontLink.crossOrigin = 'anonymous';
document.head.appendChild(fontLink);
// 預連接到 CDN
const preconnect = document.createElement('link');
preconnect.rel = 'preconnect';
preconnect.href = 'https://cdn.kyong.com';
document.head.appendChild(preconnect);
}
// 效能監控
export function reportWebVitals(metric: any) {
// 發送到分析服務
if (window.gtag) {
window.gtag('event', metric.name, {
event_category: 'Web Vitals',
value: Math.round(metric.value),
event_label: metric.id,
non_interaction: true,
});
}
// 或發送到自己的分析端點
fetch('/api/analytics/web-vitals', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: metric.name,
value: metric.value,
id: metric.id,
navigationType: metric.navigationType,
}),
});
}
// src/lib/ab-testing.ts
import { useState, useEffect } from 'react';
export interface Experiment {
id: string;
name: string;
variants: Array<{
id: string;
name: string;
weight: number; // 權重 (0-100)
}>;
}
export interface ExperimentResult {
experimentId: string;
variantId: string;
}
/**
* A/B 測試 Hook
*/
export function useExperiment(experimentId: string): string | null {
const [variantId, setVariantId] = useState<string | null>(null);
useEffect(() => {
// 檢查是否已經分配過變體
const savedVariant = localStorage.getItem(`experiment_${experimentId}`);
if (savedVariant) {
setVariantId(savedVariant);
return;
}
// 從伺服器取得實驗配置
fetch(`/api/experiments/${experimentId}`)
.then((res) => res.json())
.then((experiment: Experiment) => {
// 加權隨機分配
const variant = selectVariant(experiment.variants);
setVariantId(variant.id);
// 儲存分配結果
localStorage.setItem(`experiment_${experimentId}`, variant.id);
// 追蹤分配事件
trackExperiment(experimentId, variant.id);
});
}, [experimentId]);
return variantId;
}
/**
* 加權隨機選擇變體
*/
function selectVariant(variants: Array<{ id: string; weight: number }>) {
const totalWeight = variants.reduce((sum, v) => sum + v.weight, 0);
let random = Math.random() * totalWeight;
for (const variant of variants) {
random -= variant.weight;
if (random <= 0) {
return variant;
}
}
return variants[0]; // fallback
}
/**
* 追蹤實驗分配
*/
function trackExperiment(experimentId: string, variantId: string) {
if (window.gtag) {
window.gtag('event', 'experiment_assignment', {
experiment_id: experimentId,
variant_id: variantId,
});
}
// 發送到自己的分析端點
fetch('/api/analytics/experiments', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
experimentId,
variantId,
timestamp: new Date().toISOString(),
}),
});
}
/**
* 追蹤轉換事件
*/
export function trackConversion(experimentId: string, eventName: string, value?: number) {
const variantId = localStorage.getItem(`experiment_${experimentId}`);
if (!variantId) return;
if (window.gtag) {
window.gtag('event', eventName, {
experiment_id: experimentId,
variant_id: variantId,
value,
});
}
fetch('/api/analytics/conversions', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
experimentId,
variantId,
eventName,
value,
timestamp: new Date().toISOString(),
}),
});
}
// 使用範例
export function HeroSectionWithABTest() {
const ctaVariant = useExperiment('hero_cta_text');
const ctaText = {
control: '免費開始使用',
variant_a: '立即免費試用',
variant_b: '開始你的旅程',
}[ctaVariant || 'control'];
const handleCTAClick = () => {
trackConversion('hero_cta_text', 'signup_click');
// ... 導航到註冊頁面
};
return (
<Button onClick={handleCTAClick}>
{ctaText}
</Button>
);
}
/**
* 轉換率優化最佳實踐
*
* 1. 清晰的價值主張
* ✅ 3 秒內讓訪客理解產品價值
* ✅ 使用數據支持(99.9% 可用性)
* ✅ 突出差異化優勢
*
* 2. 減少摩擦
* ✅ 簡化註冊流程(單頁表單)
* ✅ 社交登入選項
* ✅ 無需信用卡試用
* ✅ 自動填充
*
* 3. 建立信任
* ✅ 客戶評價與案例
* ✅ 安全認證標章
* ✅ 透明定價
* ✅ 隱私政策連結
*
* 4. 緊迫感與稀缺性
* ⚠️ 適度使用,避免過度
* ✅ 限時優惠
* ✅ 剩餘名額顯示
*
* 5. 多重 CTA
* ✅ Hero Section CTA
* ✅ Features 後的 CTA
* ✅ Pricing 中的 CTA
* ✅ 頁尾 CTA
*
* 6. 手機優先
* ✅ 響應式設計
* ✅ 觸控友善按鈕(最小 44x44px)
* ✅ 快速載入(<3s)
*
* 7. 追蹤與優化
* ✅ 設定轉換目標
* ✅ 熱圖分析(Hotjar)
* ✅ 使用者錄影
* ✅ A/B 測試
*/
// 轉換漏斗追蹤
export function setupConversionFunnel() {
// 頁面載入
trackFunnelStep('landing_page_view');
// 滾動深度
let maxScrollDepth = 0;
window.addEventListener('scroll', () => {
const scrollDepth = Math.round(
(window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100
);
if (scrollDepth > maxScrollDepth) {
maxScrollDepth = scrollDepth;
if (scrollDepth >= 25 && maxScrollDepth < 50) {
trackFunnelStep('scroll_25');
} else if (scrollDepth >= 50 && maxScrollDepth < 75) {
trackFunnelStep('scroll_50');
} else if (scrollDepth >= 75) {
trackFunnelStep('scroll_75');
}
}
});
// CTA 點擊
document.querySelectorAll('[data-cta]').forEach((button) => {
button.addEventListener('click', () => {
const ctaName = button.getAttribute('data-cta');
trackFunnelStep(`cta_click_${ctaName}`);
});
});
}
function trackFunnelStep(step: string) {
if (window.gtag) {
window.gtag('event', 'funnel_step', {
step_name: step,
timestamp: new Date().toISOString(),
});
}
}
我們今天完成了 Landing Page 的設計與實作:
Framer Motion vs CSS 動畫:
主頁橫幅載入優化:
A/B 測試最佳實踐: