本节将介绍如何使用 Tailwind CSS 开发一个现代化的响应式企业官网,包括页面布局、组件开发、响应式设计等方面。
页面布局
导航栏组件
typescript
// components/Navbar.tsx
import { useState } from 'react';
const Navbar = () => {
const [isOpen, setIsOpen] = useState(false);
return (
<nav className="bg-white shadow-lg fixed w-full top-0 z-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between h-16">
{/* Logo */}
<div className="flex-shrink-0 flex items-center">
<img
className="h-8 w-auto"
src="/logo.svg"
alt="Company Logo"
/>
</div>
{/* Desktop Menu */}
<div className="hidden md:flex md:items-center md:space-x-8">
<a href="#" className="text-gray-700 hover:text-gray-900 px-3 py-2 text-sm font-medium">
首页
</a>
<a href="#" className="text-gray-700 hover:text-gray-900 px-3 py-2 text-sm font-medium">
产品
</a>
<a href="#" className="text-gray-700 hover:text-gray-900 px-3 py-2 text-sm font-medium">
解决方案
</a>
<a href="#" className="text-gray-700 hover:text-gray-900 px-3 py-2 text-sm font-medium">
关于我们
</a>
<button className="ml-8 bg-blue-500 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-blue-600 transition-colors">
联系我们
</button>
</div>
{/* Mobile Menu Button */}
<div className="md:hidden flex items-center">
<button
onClick={() => setIsOpen(!isOpen)}
className="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100"
>
<svg
className={`${isOpen ? 'hidden' : 'block'} h-6 w-6`}
stroke="currentColor"
fill="none"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
<svg
className={`${isOpen ? 'block' : 'hidden'} h-6 w-6`}
stroke="currentColor"
fill="none"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
</div>
</div>
{/* Mobile Menu */}
<div className={`${isOpen ? 'block' : 'hidden'} md:hidden`}>
<div className="px-2 pt-2 pb-3 space-y-1">
<a href="#" className="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50">
首页
</a>
<a href="#" className="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50">
产品
</a>
<a href="#" className="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50">
解决方案
</a>
<a href="#" className="block px-3 py-2 rounded-md text-base font-medium text-gray-700 hover:text-gray-900 hover:bg-gray-50">
关于我们
</a>
<button className="w-full bg-blue-500 text-white px-4 py-2 rounded-lg text-base font-medium hover:bg-blue-600 transition-colors">
联系我们
</button>
</div>
</div>
</nav>
);
};
Hero 区域
typescript
// components/Hero.tsx
const Hero = () => {
return (
<div className="relative bg-white overflow-hidden">
<div className="max-w-7xl mx-auto">
<div className="relative z-10 pb-8 bg-white sm:pb-16 md:pb-20 lg:max-w-2xl lg:w-full lg:pb-28 xl:pb-32">
<main className="mt-10 mx-auto max-w-7xl px-4 sm:mt-12 sm:px-6 md:mt-16 lg:mt-20 lg:px-8 xl:mt-28">
<div className="sm:text-center lg:text-left">
<h1 className="text-4xl tracking-tight font-extrabold text-gray-900 sm:text-5xl md:text-6xl">
<span className="block">打造现代化的</span>
<span className="block text-blue-600">企业数字体验</span>
</h1>
<p className="mt-3 text-base text-gray-500 sm:mt-5 sm:text-lg sm:max-w-xl sm:mx-auto md:mt-5 md:text-xl lg:mx-0">
我们提供全方位的数字化解决方案,帮助企业实现数字化转型,提升运营效率,创造更大的商业价值。
</p>
<div className="mt-5 sm:mt-8 sm:flex sm:justify-center lg:justify-start">
<div className="rounded-md shadow">
<a
href="#"
className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 md:py-4 md:text-lg md:px-10"
>
开始使用
</a>
</div>
<div className="mt-3 sm:mt-0 sm:ml-3">
<a
href="#"
className="w-full flex items-center justify-center px-8 py-3 border border-transparent text-base font-medium rounded-md text-blue-700 bg-blue-100 hover:bg-blue-200 md:py-4 md:text-lg md:px-10"
>
了解更多
</a>
</div>
</div>
</div>
</main>
</div>
</div>
<div className="lg:absolute lg:inset-y-0 lg:right-0 lg:w-1/2">
<img
className="h-56 w-full object-cover sm:h-72 md:h-96 lg:w-full lg:h-full"
src="/hero-image.jpg"
alt="Hero Image"
/>
</div>
</div>
);
};
功能组件
特性展示组件
typescript
// components/Features.tsx
const features = [
{
title: '快速部署',
description: '提供一站式部署解决方案,快速上线您的业务系统。',
icon: (
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
),
},
{
title: '安全可靠',
description: '采用业界领先的安全防护措施,保障您的数据安全。',
icon: (
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
</svg>
),
},
// ... 更多特性
];
const Features = () => {
return (
<div className="py-12 bg-white">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="lg:text-center">
<h2 className="text-base text-blue-600 font-semibold tracking-wide uppercase">特性</h2>
<p className="mt-2 text-3xl leading-8 font-extrabold tracking-tight text-gray-900 sm:text-4xl">
更好的解决方案
</p>
<p className="mt-4 max-w-2xl text-xl text-gray-500 lg:mx-auto">
我们提供全面的解决方案,满足您的各种业务需求。
</p>
</div>
<div className="mt-10">
<div className="space-y-10 md:space-y-0 md:grid md:grid-cols-2 md:gap-x-8 md:gap-y-10">
{features.map((feature) => (
<div key={feature.title} className="relative">
<div className="absolute flex items-center justify-center h-12 w-12 rounded-md bg-blue-500 text-white">
{feature.icon}
</div>
<p className="ml-16 text-lg leading-6 font-medium text-gray-900">{feature.title}</p>
<dd className="mt-2 ml-16 text-base text-gray-500">{feature.description}</dd>
</div>
))}
</div>
</div>
</div>
</div>
);
};
客户案例组件
typescript
// components/Cases.tsx
const cases = [
{
title: '企业A的数字化转型',
description: '帮助企业A实现了全面的数字化转型,提升了30%的运营效率。',
image: '/case-1.jpg',
},
// ... 更多案例
];
const Cases = () => {
return (
<div className="bg-gray-50 py-12 lg:py-20">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="text-center">
<h2 className="text-3xl font-extrabold tracking-tight text-gray-900 sm:text-4xl">
成功案例
</h2>
<p className="mt-4 max-w-2xl text-xl text-gray-500 lg:mx-auto">
看看我们如何帮助客户实现业务增长。
</p>
</div>
<div className="mt-12 grid gap-8 md:grid-cols-2 lg:grid-cols-3">
{cases.map((case_) => (
<div
key={case_.title}
className="flex flex-col rounded-lg shadow-lg overflow-hidden"
>
<div className="flex-shrink-0">
<img
className="h-48 w-full object-cover"
src={case_.image}
alt={case_.title}
/>
</div>
<div className="flex-1 bg-white p-6 flex flex-col justify-between">
<div className="flex-1">
<h3 className="text-xl font-semibold text-gray-900">
{case_.title}
</h3>
<p className="mt-3 text-base text-gray-500">
{case_.description}
</p>
</div>
<div className="mt-6">
<a
href="#"
className="text-base font-semibold text-blue-600 hover:text-blue-500"
>
了解更多 →
</a>
</div>
</div>
</div>
))}
</div>
</div>
</div>
);
};
响应式优化
媒体查询策略
typescript
// styles/breakpoints.ts
export const breakpoints = {
sm: '640px', // 小屏幕设备
md: '768px', // 平板设备
lg: '1024px', // 小型笔记本
xl: '1280px', // 大型笔记本
'2xl': '1536px' // 桌面设备
};
// 使用示例
<div className="
grid
grid-cols-1
gap-6
sm:grid-cols-2
lg:grid-cols-3
xl:grid-cols-4
">
{/* 内容 */}
</div>
图片响应式处理
typescript
// components/ResponsiveImage.tsx
interface ResponsiveImageProps {
src: string;
alt: string;
className?: string;
}
const ResponsiveImage: React.FC<ResponsiveImageProps> = ({
src,
alt,
className = ''
}) => {
return (
<picture>
<source
media="(min-width: 1024px)"
srcSet={`${src}?w=1200`}
/>
<source
media="(min-width: 768px)"
srcSet={`${src}?w=800`}
/>
<img
src={`${src}?w=400`}
alt={alt}
className={`w-full h-auto ${className}`}
loading="lazy"
/>
</picture>
);
};
// 使用示例
<ResponsiveImage
src="/hero-image.jpg"
alt="Hero Image"
className="rounded-lg shadow-lg"
/>
响应式导航
typescript
// hooks/useResponsiveNav.ts
import { useState, useEffect } from 'react';
export const useResponsiveNav = () => {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isMobile, setIsMobile] = useState(false);
useEffect(() => {
const checkWidth = () => {
setIsMobile(window.innerWidth < 768);
if (window.innerWidth >= 768) {
setIsMenuOpen(false);
}
};
window.addEventListener('resize', checkWidth);
checkWidth();
return () => window.removeEventListener('resize', checkWidth);
}, []);
return {
isMenuOpen,
setIsMenuOpen,
isMobile
};
};
性能优化
图片懒加载
typescript
// components/LazyImage.tsx
import { useEffect, useRef, useState } from 'react';
interface LazyImageProps {
src: string;
alt: string;
className?: string;
placeholder?: string;
}
const LazyImage: React.FC<LazyImageProps> = ({
src,
alt,
className = '',
placeholder = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
}) => {
const [isLoaded, setIsLoaded] = useState(false);
const [imageSrc, setImageSrc] = useState(placeholder);
const imageRef = useRef<HTMLImageElement>(null);
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
setImageSrc(src);
observer.unobserve(entry.target);
}
});
},
{
rootMargin: '50px'
}
);
if (imageRef.current) {
observer.observe(imageRef.current);
}
return () => {
if (imageRef.current) {
observer.unobserve(imageRef.current);
}
};
}, [src]);
return (
<img
ref={imageRef}
src={imageSrc}
alt={alt}
className={`
transition-opacity duration-300
${isLoaded ? 'opacity-100' : 'opacity-0'}
${className}
`}
onLoad={() => setIsLoaded(true)}
/>
);
};
组件优化
typescript
// components/OptimizedCarousel.tsx
import { memo } from 'react';
interface CarouselProps {
items: Array<{
id: string;
image: string;
title: string;
}>;
}
const OptimizedCarousel = memo(({ items }: CarouselProps) => {
return (
<div className="relative overflow-hidden">
<div className="flex snap-x snap-mandatory overflow-x-auto">
{items.map((item) => (
<div
key={item.id}
className="snap-start flex-shrink-0 w-full md:w-1/2 lg:w-1/3"
>
<LazyImage
src={item.image}
alt={item.title}
className="w-full h-64 object-cover"
/>
<h3 className="mt-2 text-lg font-medium text-gray-900">
{item.title}
</h3>
</div>
))}
</div>
</div>
);
});
OptimizedCarousel.displayName = 'OptimizedCarousel';
动画效果
滚动动画
typescript
// hooks/useScrollAnimation.ts
import { useEffect, useRef } from 'react';
export const useScrollAnimation = (options = {}) => {
const elementRef = useRef<HTMLElement>(null);
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('animate-fade-in');
observer.unobserve(entry.target);
}
});
}, options);
if (elementRef.current) {
observer.observe(elementRef.current);
}
return () => {
if (elementRef.current) {
observer.unobserve(elementRef.current);
}
};
}, []);
return elementRef;
};
// 使用示例
const Section = () => {
const animationRef = useScrollAnimation({
threshold: 0.1
});
return (
<div
ref={animationRef}
className="opacity-0 transition-opacity duration-1000"
>
{/* 内容 */}
</div>
);
};
最佳实践
-
响应式设计
- 移动优先设计
- 合理使用断点
- 灵活的布局系统
-
性能优化
- 图片优化
- 组件懒加载
- 动画性能
-
用户体验
- 交互反馈
- 加载状态
- 平滑过渡
-
开发建议
- 组件复用
- 样式模块化
- 持续优化
-
测试
- 响应式测试
- 性能测试
- 兼容性测试