🛣️ 引言:让系统动起来

在第二篇教程中,我们完成了后台管理系统的基础骨架,包括侧边栏和头部布局。目前,侧边栏上的链接还是占位符。本篇我们将利用 Next.js App Router 的强大功能,实现页面间的快速导航,并让侧边栏能够根据当前页面高亮显示选中项,提供更好的用户体验。

本篇目标(教程三):

  • 利用 App Router 的文件系统路由创建 Dashboard 页面。
  • 将侧边栏的占位符链接替换为 Next.js 的 <Link> 组件。
  • 使用 usePathname Hook 实现活动链接的高亮显示。

一、创建仪表盘页面 (Dashboard)

在 App Router 中,我们通过创建文件夹来定义路由段。我们的后台系统主页应该是仪表盘,路径为 /dashboard。

1. 创建路由文件夹

在 app 目录下创建一个 dashboard 文件夹,并在其中创建 page.tsx 文件。

mkdir app/dashboard
touch app/dashboard/page.tsx

2. 编写 Dashboard 内容

在 app/dashboard/page.tsx 中编写一个简单的组件作为仪表盘的初始内容。

// app/dashboard/page.tsx

import React from 'react';

// 默认导出组件,Next.js 会将其渲染为 /dashboard 路径下的页面
export default function DashboardPage() {
  return (
    <div>
      <h1 className="text-3xl font-bold mb-6 text-gray-800">📊 仪表盘总览</h1>
      
      {/* 简单的卡片示例 */}
      <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
        <div className="bg-white p-6 rounded-lg shadow-md">
          <p className="text-sm text-gray-500">总文章数</p>
          <p className="text-3xl font-semibold text-indigo-600">128</p>
        </div>
        <div className="bg-white p-6 rounded-lg shadow-md">
          <p className="text-sm text-gray-500">待审核评论</p>
          <p className="text-3xl font-semibold text-red-500">12</p>
        </div>
        <div className="bg-white p-6 rounded-lg shadow-md">
          <p className="text-sm text-gray-500">今日访问量</p>
          <p className="text-3xl font-semibold text-green-600">4520</p>
        </div>
      </div>
    </div>
  );
}

3. 重定向根路径

目前我们的根路径 / 仍然是 Next.js 的默认欢迎页。为了让用户一进来就看到后台系统,我们可以在 / 路径下进行重定向。

修改 app/page.tsx 文件,将其内容替换为重定向:

// app/page.tsx

import { redirect } from 'next/navigation';

export default function Home() {
  // 直接重定向到仪表盘页面
  redirect('/dashboard');
}

二、使用 <Link> 实现客户端导航

普通的 HTML <a> 标签会导致页面完全刷新,这在单页应用 (SPA) 体验中是不可取的。Next.js 的 <Link> 组件则实现了客户端路由跳转,提供流畅的页面切换体验。

1. 修改 Sidebar 组件

我们需要引入 next/link 和 next/navigation 中的 usePathname。

⚠️ 注意: usePathname 是一个 Client Component Hook,因此我们必须在文件顶部添加 "use client" 指令。

修改 components/Sidebar.tsx:

// components/Sidebar.tsx

"use client"; // 声明为客户端组件

import React from 'react';
import Link from 'next/link'; // 引入 Link
import { usePathname } from 'next/navigation'; // 引入 usePathname

const navItems = [
  { name: '仪表盘', href: '/dashboard', icon: '📊' },
  { name: '文章管理', href: '/posts', icon: '✍️' },
  { name: '评论管理', href: '/comments', icon: '💬' },
  { name: '用户设置', href: '/settings', icon: '⚙️' },
];

const Sidebar: React.FC = () => {
  const pathname = usePathname(); // 获取当前路由路径

  return (
    <div className="w-64 bg-gray-800 h-screen fixed top-0 left-0 p-4 text-white z-10">
      {/* LOGO 区域 */}
      <div className="text-2xl font-bold mb-8 border-b border-gray-700 pb-4">
        博客后台系统
      </div>
      
      {/* 导航 */}
      <nav className="space-y-2">
        {navItems.map((item) => {
          // 检查当前路径是否匹配导航项的 href,从而判断是否选中
          // 这里使用 item.href === pathname 进行精确匹配
          const isActive = item.href === pathname;

          return (
            <Link 
              key={item.name}
              href={item.href} 
              className={`
                block p-3 rounded-lg transition duration-150 flex items-center space-x-3
                ${isActive 
                  ? 'bg-indigo-600 font-semibold shadow-md' // 选中时的样式:突出颜色
                  : 'hover:bg-gray-700' // 未选中时的样式
                }
              `}
            >
              <span className="text-xl">{item.icon}</span>
              <span>{item.name}</span>
            </Link>
          );
        })}
      </nav>
    </div>
  );
};

export default Sidebar;

3. 运行与验证

重新运行 npm run dev:

  1. 访问 http://localhost:3000,它会自动重定向到 /dashboard。
  2. 侧边栏的“仪表盘”链接应该被高亮显示(背景色变为 bg-indigo-600)。
  3. 点击其他链接(如“文章管理”),虽然页面会显示 404,但 URL 已经变化,且侧边栏的选中状态也会相应变化。

💡 总结与预告

在本篇教程中,我们成功完成了:

  • ✅ 创建了 /dashboard 页面并实现了根路径的重定向。
  • ✅ 将侧边栏的导航替换为 Next.js 的 <Link> 组件,实现了客户端路由跳转。
  • ✅ 利用 usePathname Hook 实现了活动链接的高亮显示,大大提升了用户体验。

在下一篇教程中,我们将开始进入后台系统的核心功能区——文章管理。我们将:

  1. 创建文章列表 /posts 页面。
  2. 介绍并使用 Server Actions 或传统的 Route Handlers 来模拟后端 API 接口。
  3. 展示如何在 Next.js 中进行简单的数据获取(模拟)。

更多推荐