第七章:项目实战 - 第二节 - Tailwind CSS 响应式官网开发

本节将介绍如何使用 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>
  );
};

最佳实践

  1. 响应式设计

    • 移动优先设计
    • 合理使用断点
    • 灵活的布局系统
  2. 性能优化

    • 图片优化
    • 组件懒加载
    • 动画性能
  3. 用户体验

    • 交互反馈
    • 加载状态
    • 平滑过渡
  4. 开发建议

    • 组件复用
    • 样式模块化
    • 持续优化
  5. 测试

    • 响应式测试
    • 性能测试
    • 兼容性测试
相关推荐
Enti7c1 小时前
什么是 jQuery
前端·javascript·jquery
taopi20242 小时前
若依vue plus环境搭建
前端·javascript·vue.js
十八朵郁金香3 小时前
深入浅出理解编译器:前端视角
开发语言·前端·javascript
huangkaihao3 小时前
解锁Vue超能力:深度解析Render函数与高效渲染的奥秘
前端·vue.js·前端框架
dawnkylin3 小时前
CSS 浮动布局
css·float·布局·浮动
桂月二二3 小时前
Island架构与部分水合技术解析:下一代Web应用的性能突围
前端·架构
XiaoLeisj5 小时前
【CSS—前端快速入门】CSS 常用样式
前端·css
GISer_Jing5 小时前
[React]Render Props、自定义Hooks和Context API优化详解
前端·javascript·react.js
南城巷陌6 小时前
HTTP 协议的发展历程:从 HTTP/1.0 到 HTTP/2.0
前端·网络·网络协议·http·node.js
Kevin1712066 小时前
前端依赖nrm镜像管理工具
前端