本章概述
在前面的章节中,我们学习了 Tailwind CSS 的各种特性和技术。本章将通过一个完整的实战项目,综合运用所学知识,构建一个现代化的响应式网站。
项目规划
项目概述
我们将构建一个现代化的 SaaS 产品着陆页,包含以下功能:
- 响应式设计
- 现代化 UI 组件
- 动画效果
- 暗色模式支持
- 性能优化
技术栈
- 前端框架: HTML5 + Vanilla JavaScript
- CSS 框架: Tailwind CSS
- 构建工具: Vite
- 图标库: Heroicons
- 字体: Inter
项目结构
saas-landing/
├── src/
│ ├── index.html
│ ├── styles/
│ │ ├── main.css
│ │ └── components.css
│ ├── js/
│ │ ├── main.js
│ │ └── theme.js
│ └── assets/
│ └── images/
├── tailwind.config.js
├── package.json
└── vite.config.js
环境搭建
1. 初始化项目
# 创建项目目录
mkdir saas-landing
cd saas-landing
# 初始化 npm 项目
npm init -y
# 安装依赖
npm install -D tailwindcss vite autoprefixer postcss
npm install @heroicons/react
# 初始化 Tailwind CSS
npx tailwindcss init -p
2. 配置 Tailwind CSS
// tailwind.config.js
module.exports = {
content: ['./src/**/*.{html,js}'],
darkMode: 'class',
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'sans-serif'],
},
colors: {
primary: {
50: '#eff6ff',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
},
gray: {
900: '#111827',
},
},
animation: {
'fade-in': 'fadeIn 0.5s ease-in-out',
'slide-up': 'slideUp 0.5s ease-out',
'bounce-slow': 'bounce 2s infinite',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
slideUp: {
'0%': { transform: 'translateY(20px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
},
},
},
plugins: [],
}
3. 配置 Vite
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
root: 'src',
build: {
outDir: '../dist',
},
})
页面结构设计
1. HTML 基础结构
<!-- src/index.html -->
<!DOCTYPE html>
<html lang="zh-CN" class="scroll-smooth">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SaaS Landing - 现代化解决方案</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="./styles/main.css">
</head>
<body class="font-sans antialiased bg-white dark:bg-gray-900 text-gray-900 dark:text-white transition-colors duration-300">
<!-- 导航栏 -->
<nav class="fixed top-0 w-full bg-white/80 dark:bg-gray-900/80 backdrop-blur-md border-b border-gray-200 dark:border-gray-700 z-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-16">
<!-- Logo -->
<div class="flex items-center">
<div class="flex-shrink-0">
<h1 class="text-2xl font-bold text-primary-600 dark:text-primary-400">SaaS</h1>
</div>
</div>
<!-- 桌面导航 -->
<div class="hidden md:block">
<div class="ml-10 flex items-baseline space-x-4">
<a href="#features" class="text-gray-600 dark:text-gray-300 hover:text-primary-600 dark:hover:text-primary-400 px-3 py-2 rounded-md text-sm font-medium transition-colors">功能</a>
<a href="#pricing" class="text-gray-600 dark:text-gray-300 hover:text-primary-600 dark:hover:text-primary-400 px-3 py-2 rounded-md text-sm font-medium transition-colors">定价</a>
<a href="#about" class="text-gray-600 dark:text-gray-300 hover:text-primary-600 dark:hover:text-primary-400 px-3 py-2 rounded-md text-sm font-medium transition-colors">关于</a>
<a href="#contact" class="text-gray-600 dark:text-gray-300 hover:text-primary-600 dark:hover:text-primary-400 px-3 py-2 rounded-md text-sm font-medium transition-colors">联系</a>
</div>
</div>
<!-- 右侧按钮 -->
<div class="flex items-center space-x-4">
<!-- 主题切换按钮 -->
<button id="theme-toggle" class="p-2 rounded-lg bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors">
<svg class="w-5 h-5 hidden dark:block" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" clip-rule="evenodd"></path>
</svg>
<svg class="w-5 h-5 block dark:hidden" fill="currentColor" viewBox="0 0 20 20">
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path>
</svg>
</button>
<!-- 登录按钮 -->
<a href="#" class="text-gray-600 dark:text-gray-300 hover:text-primary-600 dark:hover:text-primary-400 px-3 py-2 rounded-md text-sm font-medium transition-colors">登录</a>
<!-- 注册按钮 -->
<a href="#" class="bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors">免费试用</a>
<!-- 移动端菜单按钮 -->
<button id="mobile-menu-button" class="md:hidden p-2 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
</div>
</div>
</div>
<!-- 移动端菜单 -->
<div id="mobile-menu" class="md:hidden hidden bg-white dark:bg-gray-900 border-t border-gray-200 dark:border-gray-700">
<div class="px-2 pt-2 pb-3 space-y-1">
<a href="#features" class="block text-gray-600 dark:text-gray-300 hover:text-primary-600 dark:hover:text-primary-400 px-3 py-2 rounded-md text-base font-medium transition-colors">功能</a>
<a href="#pricing" class="block text-gray-600 dark:text-gray-300 hover:text-primary-600 dark:hover:text-primary-400 px-3 py-2 rounded-md text-base font-medium transition-colors">定价</a>
<a href="#about" class="block text-gray-600 dark:text-gray-300 hover:text-primary-600 dark:hover:text-primary-400 px-3 py-2 rounded-md text-base font-medium transition-colors">关于</a>
<a href="#contact" class="block text-gray-600 dark:text-gray-300 hover:text-primary-600 dark:hover:text-primary-400 px-3 py-2 rounded-md text-base font-medium transition-colors">联系</a>
</div>
</div>
</nav>
<!-- 主要内容 -->
<main class="pt-16">
<!-- Hero 区域 -->
<section class="relative bg-gradient-to-br from-primary-50 to-blue-100 dark:from-gray-900 dark:to-gray-800 overflow-hidden">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-24 lg:py-32">
<div class="text-center">
<h1 class="text-4xl sm:text-5xl lg:text-6xl font-bold text-gray-900 dark:text-white mb-6 animate-fade-in">
现代化的
<span class="text-primary-600 dark:text-primary-400">SaaS 解决方案</span>
</h1>
<p class="text-xl text-gray-600 dark:text-gray-300 mb-8 max-w-3xl mx-auto animate-slide-up">
提供企业级的云服务平台,帮助您的业务快速增长。安全、可靠、易用的解决方案,让您专注于核心业务。
</p>
<div class="flex flex-col sm:flex-row gap-4 justify-center animate-slide-up">
<a href="#" class="bg-primary-600 hover:bg-primary-700 text-white px-8 py-3 rounded-lg text-lg font-medium transition-all duration-300 transform hover:scale-105 shadow-lg hover:shadow-xl">
免费开始
</a>
<a href="#" class="border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 px-8 py-3 rounded-lg text-lg font-medium transition-all duration-300">
观看演示
</a>
</div>
</div>
</div>
<!-- 背景装饰 -->
<div class="absolute top-0 left-0 w-full h-full overflow-hidden pointer-events-none">
<div class="absolute -top-40 -right-40 w-80 h-80 bg-primary-200 dark:bg-primary-900 rounded-full opacity-20 animate-bounce-slow"></div>
<div class="absolute -bottom-40 -left-40 w-80 h-80 bg-blue-200 dark:bg-blue-900 rounded-full opacity-20 animate-bounce-slow" style="animation-delay: 1s;"></div>
</div>
</section>
<!-- 功能特性区域 -->
<section id="features" class="py-24 bg-white dark:bg-gray-900">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center mb-16">
<h2 class="text-3xl lg:text-4xl font-bold text-gray-900 dark:text-white mb-4">
强大的功能特性
</h2>
<p class="text-xl text-gray-600 dark:text-gray-300 max-w-3xl mx-auto">
我们提供全面的功能来满足您的业务需求
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<!-- 功能卡片 1 -->
<div class="bg-white dark:bg-gray-800 p-8 rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-2 border border-gray-100 dark:border-gray-700">
<div class="w-12 h-12 bg-primary-100 dark:bg-primary-900 rounded-lg flex items-center justify-center mb-6">
<svg class="w-6 h-6 text-primary-600 dark:text-primary-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
</svg>
</div>
<h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">高性能</h3>
<p class="text-gray-600 dark:text-gray-300">基于云原生架构,提供毫秒级响应速度和99.9%的可用性保证。</p>
</div>
<!-- 功能卡片 2 -->
<div class="bg-white dark:bg-gray-800 p-8 rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-2 border border-gray-100 dark:border-gray-700">
<div class="w-12 h-12 bg-primary-100 dark:bg-primary-900 rounded-lg flex items-center justify-center mb-6">
<svg class="w-6 h-6 text-primary-600 dark:text-primary-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"></path>
</svg>
</div>
<h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">安全可靠</h3>
<p class="text-gray-600 dark:text-gray-300">企业级安全防护,数据加密传输,符合国际安全标准认证。</p>
</div>
<!-- 功能卡片 3 -->
<div class="bg-white dark:bg-gray-800 p-8 rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-2 border border-gray-100 dark:border-gray-700">
<div class="w-12 h-12 bg-primary-100 dark:bg-primary-900 rounded-lg flex items-center justify-center mb-6">
<svg class="w-6 h-6 text-primary-600 dark:text-primary-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"></path>
</svg>
</div>
<h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">易于使用</h3>
<p class="text-gray-600 dark:text-gray-300">直观的用户界面,简单的操作流程,让您快速上手并提高工作效率。</p>
</div>
<!-- 功能卡片 4 -->
<div class="bg-white dark:bg-gray-800 p-8 rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-2 border border-gray-100 dark:border-gray-700">
<div class="w-12 h-12 bg-primary-100 dark:bg-primary-900 rounded-lg flex items-center justify-center mb-6">
<svg class="w-6 h-6 text-primary-600 dark:text-primary-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 12l3-3 3 3 4-4M8 21l4-4 4 4M3 4h18M4 4h16v12a1 1 0 01-1 1H5a1 1 0 01-1-1V4z"></path>
</svg>
</div>
<h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">数据分析</h3>
<p class="text-gray-600 dark:text-gray-300">实时数据监控和深度分析,帮助您做出更明智的业务决策。</p>
</div>
<!-- 功能卡片 5 -->
<div class="bg-white dark:bg-gray-800 p-8 rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-2 border border-gray-100 dark:border-gray-700">
<div class="w-12 h-12 bg-primary-100 dark:bg-primary-900 rounded-lg flex items-center justify-center mb-6">
<svg class="w-6 h-6 text-primary-600 dark:text-primary-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"></path>
</svg>
</div>
<h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">团队协作</h3>
<p class="text-gray-600 dark:text-gray-300">支持多人协作,权限管理,让团队工作更加高效有序。</p>
</div>
<!-- 功能卡片 6 -->
<div class="bg-white dark:bg-gray-800 p-8 rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-2 border border-gray-100 dark:border-gray-700">
<div class="w-12 h-12 bg-primary-100 dark:bg-primary-900 rounded-lg flex items-center justify-center mb-6">
<svg class="w-6 h-6 text-primary-600 dark:text-primary-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 5.636l-3.536 3.536m0 5.656l3.536 3.536M9.172 9.172L5.636 5.636m3.536 9.192L5.636 18.364M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-5 0a4 4 0 11-8 0 4 4 0 018 0z"></path>
</svg>
</div>
<h3 class="text-xl font-semibold text-gray-900 dark:text-white mb-4">24/7 支持</h3>
<p class="text-gray-600 dark:text-gray-300">全天候技术支持,专业团队随时为您解决问题和提供帮助。</p>
</div>
</div>
</div>
</section>
<!-- 定价区域 -->
<section id="pricing" class="py-24 bg-gray-50 dark:bg-gray-800">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center mb-16">
<h2 class="text-3xl lg:text-4xl font-bold text-gray-900 dark:text-white mb-4">
选择适合您的方案
</h2>
<p class="text-xl text-gray-600 dark:text-gray-300 max-w-3xl mx-auto">
灵活的定价方案,满足不同规模企业的需求
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<!-- 基础版 -->
<div class="bg-white dark:bg-gray-900 rounded-xl shadow-lg p-8 border border-gray-200 dark:border-gray-700">
<div class="text-center mb-8">
<h3 class="text-2xl font-bold text-gray-900 dark:text-white mb-2">基础版</h3>
<p class="text-gray-600 dark:text-gray-300 mb-4">适合小型团队</p>
<div class="text-4xl font-bold text-gray-900 dark:text-white">
¥99
<span class="text-lg font-normal text-gray-600 dark:text-gray-300">/月</span>
</div>
</div>
<ul class="space-y-4 mb-8">
<li class="flex items-center">
<svg class="w-5 h-5 text-green-500 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
</svg>
<span class="text-gray-600 dark:text-gray-300">最多 5 个用户</span>
</li>
<li class="flex items-center">
<svg class="w-5 h-5 text-green-500 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
</svg>
<span class="text-gray-600 dark:text-gray-300">10GB 存储空间</span>
</li>
<li class="flex items-center">
<svg class="w-5 h-5 text-green-500 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
</svg>
<span class="text-gray-600 dark:text-gray-300">基础功能</span>
</li>
<li class="flex items-center">
<svg class="w-5 h-5 text-green-500 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
</svg>
<span class="text-gray-600 dark:text-gray-300">邮件支持</span>
</li>
</ul>
<a href="#" class="w-full bg-gray-900 dark:bg-gray-700 text-white py-3 px-6 rounded-lg font-medium hover:bg-gray-800 dark:hover:bg-gray-600 transition-colors text-center block">
开始使用
</a>
</div>
<!-- 专业版 (推荐) -->
<div class="bg-white dark:bg-gray-900 rounded-xl shadow-xl p-8 border-2 border-primary-500 relative transform scale-105">
<div class="absolute -top-4 left-1/2 transform -translate-x-1/2">
<span class="bg-primary-500 text-white px-4 py-1 rounded-full text-sm font-medium">推荐</span>
</div>
<div class="text-center mb-8">
<h3 class="text-2xl font-bold text-gray-900 dark:text-white mb-2">专业版</h3>
<p class="text-gray-600 dark:text-gray-300 mb-4">适合中型企业</p>
<div class="text-4xl font-bold text-gray-900 dark:text-white">
¥299
<span class="text-lg font-normal text-gray-600 dark:text-gray-300">/月</span>
</div>
</div>
<ul class="space-y-4 mb-8">
<li class="flex items-center">
<svg class="w-5 h-5 text-green-500 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
</svg>
<span class="text-gray-600 dark:text-gray-300">最多 25 个用户</span>
</li>
<li class="flex items-center">
<svg class="w-5 h-5 text-green-500 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
</svg>
<span class="text-gray-600 dark:text-gray-300">100GB 存储空间</span>
</li>
<li class="flex items-center">
<svg class="w-5 h-5 text-green-500 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
</svg>
<span class="text-gray-600 dark:text-gray-300">高级功能</span>
</li>
<li class="flex items-center">
<svg class="w-5 h-5 text-green-500 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
</svg>
<span class="text-gray-600 dark:text-gray-300">优先支持</span>
</li>
<li class="flex items-center">
<svg class="w-5 h-5 text-green-500 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
</svg>
<span class="text-gray-600 dark:text-gray-300">API 访问</span>
</li>
</ul>
<a href="#" class="w-full bg-primary-600 text-white py-3 px-6 rounded-lg font-medium hover:bg-primary-700 transition-colors text-center block">
开始使用
</a>
</div>
<!-- 企业版 -->
<div class="bg-white dark:bg-gray-900 rounded-xl shadow-lg p-8 border border-gray-200 dark:border-gray-700">
<div class="text-center mb-8">
<h3 class="text-2xl font-bold text-gray-900 dark:text-white mb-2">企业版</h3>
<p class="text-gray-600 dark:text-gray-300 mb-4">适合大型企业</p>
<div class="text-4xl font-bold text-gray-900 dark:text-white">
¥999
<span class="text-lg font-normal text-gray-600 dark:text-gray-300">/月</span>
</div>
</div>
<ul class="space-y-4 mb-8">
<li class="flex items-center">
<svg class="w-5 h-5 text-green-500 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
</svg>
<span class="text-gray-600 dark:text-gray-300">无限用户</span>
</li>
<li class="flex items-center">
<svg class="w-5 h-5 text-green-500 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
</svg>
<span class="text-gray-600 dark:text-gray-300">1TB 存储空间</span>
</li>
<li class="flex items-center">
<svg class="w-5 h-5 text-green-500 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
</svg>
<span class="text-gray-600 dark:text-gray-300">全部功能</span>
</li>
<li class="flex items-center">
<svg class="w-5 h-5 text-green-500 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
</svg>
<span class="text-gray-600 dark:text-gray-300">专属客服</span>
</li>
<li class="flex items-center">
<svg class="w-5 h-5 text-green-500 mr-3" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"></path>
</svg>
<span class="text-gray-600 dark:text-gray-300">定制开发</span>
</li>
</ul>
<a href="#" class="w-full bg-gray-900 dark:bg-gray-700 text-white py-3 px-6 rounded-lg font-medium hover:bg-gray-800 dark:hover:bg-gray-600 transition-colors text-center block">
联系销售
</a>
</div>
</div>
</div>
</section>
<!-- CTA 区域 -->
<section class="py-24 bg-primary-600 dark:bg-primary-700">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<h2 class="text-3xl lg:text-4xl font-bold text-white mb-4">
准备开始了吗?
</h2>
<p class="text-xl text-primary-100 mb-8 max-w-3xl mx-auto">
立即注册,享受 14 天免费试用,无需信用卡
</p>
<div class="flex flex-col sm:flex-row gap-4 justify-center">
<a href="#" class="bg-white text-primary-600 px-8 py-3 rounded-lg text-lg font-medium hover:bg-gray-100 transition-colors">
免费试用
</a>
<a href="#" class="border border-white text-white hover:bg-white hover:text-primary-600 px-8 py-3 rounded-lg text-lg font-medium transition-colors">
联系我们
</a>
</div>
</div>
</section>
</main>
<!-- 页脚 -->
<footer class="bg-gray-900 dark:bg-gray-950 text-white">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<div class="grid grid-cols-1 md:grid-cols-4 gap-8">
<div class="col-span-1 md:col-span-2">
<h3 class="text-2xl font-bold text-primary-400 mb-4">SaaS</h3>
<p class="text-gray-300 mb-4 max-w-md">
提供现代化的云服务解决方案,帮助企业实现数字化转型,提高业务效率。
</p>
<div class="flex space-x-4">
<a href="#" class="text-gray-400 hover:text-white transition-colors">
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
<path d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"/>
</svg>
</a>
<a href="#" class="text-gray-400 hover:text-white transition-colors">
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
<path d="M22.46 6c-.77.35-1.6.58-2.46.69.88-.53 1.56-1.37 1.88-2.38-.83.5-1.75.85-2.72 1.05C18.37 4.5 17.26 4 16 4c-2.35 0-4.27 1.92-4.27 4.29 0 .34.04.67.11.98C8.28 9.09 5.11 7.38 3 4.79c-.37.63-.58 1.37-.58 2.15 0 1.49.75 2.81 1.91 3.56-.71 0-1.37-.2-1.95-.5v.03c0 2.08 1.48 3.82 3.44 4.21a4.22 4.22 0 0 1-1.93.07 4.28 4.28 0 0 0 4 2.98 8.521 8.521 0 0 1-5.33 1.84c-.34 0-.68-.02-1.02-.06C3.44 20.29 5.7 21 8.12 21 16 21 20.33 14.46 20.33 8.79c0-.19 0-.37-.01-.56.84-.6 1.56-1.36 2.14-2.23z"/>
</svg>
</a>
<a href="#" class="text-gray-400 hover:text-white transition-colors">
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
</svg>
</a>
</div>
</div>
<div>
<h4 class="text-lg font-semibold mb-4">产品</h4>
<ul class="space-y-2">
<li><a href="#" class="text-gray-300 hover:text-white transition-colors">功能特性</a></li>
<li><a href="#" class="text-gray-300 hover:text-white transition-colors">定价方案</a></li>
<li><a href="#" class="text-gray-300 hover:text-white transition-colors">API 文档</a></li>
<li><a href="#" class="text-gray-300 hover:text-white transition-colors">集成</a></li>
</ul>
</div>
<div>
<h4 class="text-lg font-semibold mb-4">支持</h4>
<ul class="space-y-2">
<li><a href="#" class="text-gray-300 hover:text-white transition-colors">帮助中心</a></li>
<li><a href="#" class="text-gray-300 hover:text-white transition-colors">联系我们</a></li>
<li><a href="#" class="text-gray-300 hover:text-white transition-colors">状态页面</a></li>
<li><a href="#" class="text-gray-300 hover:text-white transition-colors">社区</a></li>
</ul>
</div>
</div>
<div class="border-t border-gray-800 mt-12 pt-8 flex flex-col md:flex-row justify-between items-center">
<p class="text-gray-400 text-sm">
© 2024 SaaS. 保留所有权利。
</p>
<div class="flex space-x-6 mt-4 md:mt-0">
<a href="#" class="text-gray-400 hover:text-white text-sm transition-colors">隐私政策</a>
<a href="#" class="text-gray-400 hover:text-white text-sm transition-colors">服务条款</a>
<a href="#" class="text-gray-400 hover:text-white text-sm transition-colors">Cookie 政策</a>
</div>
</div>
</div>
</footer>
<script src="./js/main.js"></script>
</body>
</html>
JavaScript 功能实现
1. 主题切换功能
// src/js/theme.js
class ThemeManager {
constructor() {
this.theme = localStorage.getItem('theme') || 'light'
this.init()
}
init() {
this.applyTheme()
this.bindEvents()
}
applyTheme() {
if (this.theme === 'dark') {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
}
toggleTheme() {
this.theme = this.theme === 'light' ? 'dark' : 'light'
localStorage.setItem('theme', this.theme)
this.applyTheme()
}
bindEvents() {
const themeToggle = document.getElementById('theme-toggle')
if (themeToggle) {
themeToggle.addEventListener('click', () => this.toggleTheme())
}
}
}
// 导出主题管理器
window.ThemeManager = ThemeManager
2. 移动端菜单
// src/js/mobile-menu.js
class MobileMenu {
constructor() {
this.isOpen = false
this.init()
}
init() {
this.bindEvents()
}
toggle() {
this.isOpen = !this.isOpen
const menu = document.getElementById('mobile-menu')
const button = document.getElementById('mobile-menu-button')
if (this.isOpen) {
menu.classList.remove('hidden')
button.setAttribute('aria-expanded', 'true')
} else {
menu.classList.add('hidden')
button.setAttribute('aria-expanded', 'false')
}
}
close() {
this.isOpen = false
const menu = document.getElementById('mobile-menu')
const button = document.getElementById('mobile-menu-button')
menu.classList.add('hidden')
button.setAttribute('aria-expanded', 'false')
}
bindEvents() {
const button = document.getElementById('mobile-menu-button')
if (button) {
button.addEventListener('click', () => this.toggle())
}
// 点击菜单项时关闭菜单
const menuItems = document.querySelectorAll('#mobile-menu a')
menuItems.forEach(item => {
item.addEventListener('click', () => this.close())
})
// 点击外部区域关闭菜单
document.addEventListener('click', (e) => {
const menu = document.getElementById('mobile-menu')
const button = document.getElementById('mobile-menu-button')
if (this.isOpen && !menu.contains(e.target) && !button.contains(e.target)) {
this.close()
}
})
}
}
// 导出移动端菜单
window.MobileMenu = MobileMenu
3. 滚动动画
// src/js/scroll-animations.js
class ScrollAnimations {
constructor() {
this.init()
}
init() {
this.setupIntersectionObserver()
this.setupSmoothScroll()
}
setupIntersectionObserver() {
const options = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
}
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('animate-fade-in')
}
})
}, options)
// 观察需要动画的元素
const animatedElements = document.querySelectorAll('.animate-on-scroll')
animatedElements.forEach(el => observer.observe(el))
}
setupSmoothScroll() {
// 平滑滚动到锚点
const links = document.querySelectorAll('a[href^="#"]')
links.forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault()
const targetId = link.getAttribute('href').substring(1)
const targetElement = document.getElementById(targetId)
if (targetElement) {
targetElement.scrollIntoView({
behavior: 'smooth',
block: 'start'
})
}
})
})
}
}
// 导出滚动动画
window.ScrollAnimations = ScrollAnimations
4. 主入口文件
// src/js/main.js
// 等待 DOM 加载完成
document.addEventListener('DOMContentLoaded', () => {
// 初始化主题管理器
const themeManager = new ThemeManager()
// 初始化移动端菜单
const mobileMenu = new MobileMenu()
// 初始化滚动动画
const scrollAnimations = new ScrollAnimations()
// 导航栏滚动效果
let lastScrollY = window.scrollY
const navbar = document.querySelector('nav')
window.addEventListener('scroll', () => {
const currentScrollY = window.scrollY
if (currentScrollY > 100) {
navbar.classList.add('shadow-lg')
} else {
navbar.classList.remove('shadow-lg')
}
lastScrollY = currentScrollY
})
// 添加加载动画
document.body.classList.add('animate-fade-in')
console.log('SaaS Landing Page 已加载完成')
})
CSS 样式文件
1. 主样式文件
/* src/styles/main.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* 自定义基础样式 */
@layer base {
html {
scroll-behavior: smooth;
}
body {
@apply font-sans antialiased;
}
}
/* 自定义组件样式 */
@layer components {
.btn-primary {
@apply bg-primary-600 hover:bg-primary-700 text-white px-6 py-3 rounded-lg font-medium transition-all duration-300 transform hover:scale-105 shadow-lg hover:shadow-xl;
}
.btn-secondary {
@apply border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 px-6 py-3 rounded-lg font-medium transition-all duration-300;
}
.card {
@apply bg-white dark:bg-gray-800 rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-2 border border-gray-100 dark:border-gray-700;
}
.section-padding {
@apply py-24;
}
.container-padding {
@apply max-w-7xl mx-auto px-4 sm:px-6 lg:px-8;
}
}
/* 自定义工具类 */
@layer utilities {
.text-gradient {
@apply bg-gradient-to-r from-primary-600 to-blue-600 bg-clip-text text-transparent;
}
.bg-gradient-primary {
@apply bg-gradient-to-br from-primary-50 to-blue-100 dark:from-gray-900 dark:to-gray-800;
}
.backdrop-blur-nav {
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
}
}
/* 动画样式 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes bounceIn {
0% {
opacity: 0;
transform: scale(0.3);
}
50% {
opacity: 1;
transform: scale(1.05);
}
70% {
transform: scale(0.9);
}
100% {
opacity: 1;
transform: scale(1);
}
}
/* 响应式隐藏类 */
.animate-on-scroll {
opacity: 0;
transform: translateY(30px);
transition: all 0.6s ease-out;
}
.animate-on-scroll.animate-fade-in {
opacity: 1;
transform: translateY(0);
}
/* 加载状态 */
.loading {
@apply animate-pulse;
}
/* 自定义滚动条 */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
@apply bg-gray-100 dark:bg-gray-800;
}
::-webkit-scrollbar-thumb {
@apply bg-gray-300 dark:bg-gray-600 rounded-full;
}
::-webkit-scrollbar-thumb:hover {
@apply bg-gray-400 dark:bg-gray-500;
}
2. 组件样式文件
/* src/styles/components.css */
/* 导航栏组件 */
.navbar {
@apply fixed top-0 w-full bg-white/80 dark:bg-gray-900/80 backdrop-blur-md border-b border-gray-200 dark:border-gray-700 z-50 transition-all duration-300;
}
.navbar.scrolled {
@apply shadow-lg bg-white/95 dark:bg-gray-900/95;
}
/* 卡片组件 */
.feature-card {
@apply bg-white dark:bg-gray-800 p-8 rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-2 border border-gray-100 dark:border-gray-700;
}
.feature-card:hover {
@apply scale-105;
}
/* 定价卡片 */
.pricing-card {
@apply bg-white dark:bg-gray-900 rounded-xl shadow-lg p-8 border border-gray-200 dark:border-gray-700 transition-all duration-300;
}
.pricing-card.featured {
@apply border-2 border-primary-500 transform scale-105 shadow-xl;
}
.pricing-card.featured::before {
content: '推荐';
@apply absolute -top-4 left-1/2 transform -translate-x-1/2 bg-primary-500 text-white px-4 py-1 rounded-full text-sm font-medium;
}
/* 按钮组件 */
.btn {
@apply px-6 py-3 rounded-lg font-medium transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-offset-2;
}
.btn-primary {
@apply bg-primary-600 hover:bg-primary-700 text-white focus:ring-primary-500 transform hover:scale-105 shadow-lg hover:shadow-xl;
}
.btn-secondary {
@apply border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-800 focus:ring-gray-500;
}
.btn-outline {
@apply border border-current text-current hover:bg-current hover:text-white;
}
/* 表单组件 */
.form-input {
@apply w-full px-4 py-3 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent bg-white dark:bg-gray-800 text-gray-900 dark:text-white transition-colors;
}
.form-label {
@apply block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2;
}
/* 图标组件 */
.icon {
@apply w-6 h-6;
}
.icon-sm {
@apply w-4 h-4;
}
.icon-lg {
@apply w-8 h-8;
}
/* 徽章组件 */
.badge {
@apply inline-flex items-center px-3 py-1 rounded-full text-sm font-medium;
}
.badge-primary {
@apply bg-primary-100 text-primary-800 dark:bg-primary-900 dark:text-primary-200;
}
.badge-success {
@apply bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200;
}
.badge-warning {
@apply bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200;
}
.badge-error {
@apply bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200;
}
构建配置
1. Package.json
{
"name": "saas-landing",
"version": "1.0.0",
"description": "现代化 SaaS 产品着陆页",
"main": "index.js",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"build-css": "tailwindcss -i ./src/styles/main.css -o ./dist/styles/main.css --watch"
},
"keywords": ["tailwind", "css", "saas", "landing", "responsive"],
"author": "Your Name",
"license": "MIT",
"devDependencies": {
"autoprefixer": "^10.4.16",
"postcss": "^8.4.32",
"tailwindcss": "^3.3.6",
"vite": "^5.0.8"
},
"dependencies": {
"@heroicons/react": "^2.0.18"
}
}
2. PostCSS 配置
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
性能优化
1. 图片优化
// src/js/image-optimization.js
class ImageOptimization {
constructor() {
this.init()
}
init() {
this.setupLazyLoading()
this.setupWebPSupport()
}
setupLazyLoading() {
const images = document.querySelectorAll('img[data-src]')
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target
img.src = img.dataset.src
img.classList.remove('loading')
observer.unobserve(img)
}
})
})
images.forEach(img => imageObserver.observe(img))
}
setupWebPSupport() {
const supportsWebP = () => {
const canvas = document.createElement('canvas')
return canvas.toDataURL('image/webp').indexOf('webp') > -1
}
if (supportsWebP()) {
document.documentElement.classList.add('webp')
}
}
}
// 导出图片优化
window.ImageOptimization = ImageOptimization
2. 代码分割
// src/js/code-splitting.js
class CodeSplitting {
constructor() {
this.loadedModules = new Set()
}
async loadModule(moduleName) {
if (this.loadedModules.has(moduleName)) {
return
}
try {
switch (moduleName) {
case 'analytics':
await import('./analytics.js')
break
case 'chat':
await import('./chat.js')
break
default:
console.warn(`Unknown module: ${moduleName}`)
}
this.loadedModules.add(moduleName)
} catch (error) {
console.error(`Failed to load module ${moduleName}:`, error)
}
}
loadOnDemand() {
// 滚动到页面底部时加载聊天模块
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.loadModule('chat')
observer.unobserve(entry.target)
}
})
})
const footer = document.querySelector('footer')
if (footer) {
observer.observe(footer)
}
}
}
// 导出代码分割
window.CodeSplitting = CodeSplitting
部署配置
1. 构建脚本
#!/bin/bash
# build.sh
echo "开始构建 SaaS Landing Page..."
# 清理旧的构建文件
rm -rf dist
# 构建项目
npm run build
# 复制静态资源
cp -r src/assets dist/
# 生成 sitemap
echo "生成 sitemap..."
cat > dist/sitemap.xml << EOF
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://your-domain.com/</loc>
<lastmod>$(date +%Y-%m-%d)</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
</urlset>
EOF
# 生成 robots.txt
cat > dist/robots.txt << EOF
User-agent: *
Allow: /
Sitemap: https://your-domain.com/sitemap.xml
EOF
echo "构建完成!"
2. Nginx 配置
# nginx.conf
server {
listen 80;
server_name your-domain.com;
root /var/www/saas-landing/dist;
index index.html;
# 启用 gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# HTML 文件不缓存
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# 单页应用路由
location / {
try_files $uri $uri/ /index.html;
}
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
}
测试与调试
1. 性能测试
// src/js/performance.js
class PerformanceMonitor {
constructor() {
this.metrics = {}
this.init()
}
init() {
this.measurePageLoad()
this.measureInteractions()
}
measurePageLoad() {
window.addEventListener('load', () => {
const navigation = performance.getEntriesByType('navigation')[0]
this.metrics.pageLoad = {
domContentLoaded: navigation.domContentLoadedEventEnd - navigation.domContentLoadedEventStart,
loadComplete: navigation.loadEventEnd - navigation.loadEventStart,
totalTime: navigation.loadEventEnd - navigation.fetchStart
}
console.log('页面加载性能:', this.metrics.pageLoad)
})
}
measureInteractions() {
// 测量首次输入延迟 (FID)
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
this.metrics.fid = entry.processingStart - entry.startTime
console.log('首次输入延迟:', this.metrics.fid)
}
}).observe({ type: 'first-input', buffered: true })
// 测量累积布局偏移 (CLS)
let clsValue = 0
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
if (!entry.hadRecentInput) {
clsValue += entry.value
}
}
this.metrics.cls = clsValue
console.log('累积布局偏移:', this.metrics.cls)
}).observe({ type: 'layout-shift', buffered: true })
}
getMetrics() {
return this.metrics
}
}
// 导出性能监控
window.PerformanceMonitor = PerformanceMonitor
2. 错误处理
// src/js/error-handling.js
class ErrorHandler {
constructor() {
this.init()
}
init() {
this.setupGlobalErrorHandler()
this.setupUnhandledRejectionHandler()
}
setupGlobalErrorHandler() {
window.addEventListener('error', (event) => {
this.logError({
type: 'JavaScript Error',
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno,
stack: event.error?.stack
})
})
}
setupUnhandledRejectionHandler() {
window.addEventListener('unhandledrejection', (event) => {
this.logError({
type: 'Unhandled Promise Rejection',
message: event.reason?.message || event.reason,
stack: event.reason?.stack
})
})
}
logError(error) {
console.error('错误详情:', error)
// 在生产环境中,可以发送错误到监控服务
if (process.env.NODE_ENV === 'production') {
this.sendErrorToService(error)
}
}
sendErrorToService(error) {
// 发送错误到监控服务(如 Sentry、LogRocket 等)
fetch('/api/errors', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
...error,
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent,
url: window.location.href
})
}).catch(err => {
console.error('发送错误日志失败:', err)
})
}
}
// 导出错误处理
window.ErrorHandler = ErrorHandler
项目总结
实现的功能
- 响应式设计: 适配各种设备尺寸
- 暗色模式: 支持主题切换
- 现代化 UI: 使用 Tailwind CSS 构建
- 性能优化: 图片懒加载、代码分割
- 动画效果: 平滑的交互动画
- 移动端友好: 移动端菜单和触摸优化
- SEO 优化: 语义化 HTML 和 meta 标签
- 可访问性: 键盘导航和屏幕阅读器支持
技术亮点
- 模块化架构: JavaScript 代码按功能模块组织
- 组件化 CSS: 使用 @apply 指令创建可复用组件
- 性能监控: 内置性能指标收集
- 错误处理: 完善的错误捕获和上报机制
- 构建优化: 使用 Vite 进行快速构建
最佳实践
- 代码组织: 清晰的文件结构和命名规范
- 性能优化: 图片优化、代码分割、缓存策略
- 用户体验: 加载状态、错误提示、平滑动画
- 可维护性: 模块化代码、详细注释、类型检查
- 部署配置: 自动化构建、服务器配置、监控设置
本章总结
通过本章的实战项目,我们:
- 综合运用了 Tailwind CSS 的各种特性
- 构建了一个完整的现代化网站
- 实现了响应式设计和暗色模式
- 优化了性能和用户体验
- 建立了完整的开发和部署流程
这个项目展示了如何使用 Tailwind CSS 构建生产级别的网站,包含了从设计到部署的完整流程。
练习题
- 功能扩展: 为网站添加多语言支持
- 性能优化: 实现 Service Worker 缓存策略
- 交互增强: 添加更多动画效果和微交互
- 数据集成: 连接后端 API 实现动态内容
- 测试完善: 编写单元测试和端到端测试
下一步
在下一章中,我们将学习 总结与最佳实践,回顾整个 Tailwind CSS 学习过程,总结最佳实践和常见问题解决方案。