Preact Router基础 #

一、路由概述 #

1.1 什么是路由 #

路由允许根据 URL 显示不同的组件,实现单页应用(SPA)的页面切换。

text
URL                    组件
/home        →         HomePage
/about       →         AboutPage
/users/:id   →         UserDetail

1.2 安装 #

bash
npm install preact-router

1.3 基本结构 #

jsx
import { Router } from 'preact-router';
import { HomePage, AboutPage, ContactPage } from './pages';

function App() {
  return (
    <Router>
      <HomePage path="/" />
      <AboutPage path="/about" />
      <ContactPage path="/contact" />
    </Router>
  );
}

二、基本路由 #

2.1 定义路由 #

jsx
import { Router } from 'preact-router';

function App() {
  return (
    <Router>
      <Home path="/" />
      <About path="/about" />
      <Contact path="/contact" />
      <NotFound default />
    </Router>
  );
}

function Home() {
  return <h1>Home Page</h1>;
}

function About() {
  return <h1>About Page</h1>;
}

function Contact() {
  return <h1>Contact Page</h1>;
}

function NotFound() {
  return <h1>404 - Page Not Found</h1>;
}

2.2 默认路由 #

jsx
<Router>
  <Home path="/" />
  <About path="/about" />
  <NotFound default />  {/* 匹配所有未定义的路由 */}
</Router>

2.3 布局路由 #

jsx
function App() {
  return (
    <Router>
      <Layout path="/">
        <Home path="/" />
        <About path="/about" />
      </Layout>
      <AdminLayout path="/admin">
        <Dashboard path="/" />
        <Users path="/users" />
      </AdminLayout>
    </Router>
  );
}

function Layout({ children }) {
  return (
    <div class="layout">
      <Header />
      <main>{children}</main>
      <Footer />
    </div>
  );
}

三、导航 #

jsx
import { Link } from 'preact-router/match';

function Navigation() {
  return (
    <nav>
      <Link href="/">Home</Link>
      <Link href="/about">About</Link>
      <Link href="/contact">Contact</Link>
    </nav>
  );
}

3.2 激活状态 #

jsx
import { Link } from 'preact-router/match';

function Navigation() {
  return (
    <nav>
      {/* activeClassName 自动添加激活类 */}
      <Link href="/" activeClassName="active">Home</Link>
      <Link href="/about" activeClassName="active">About</Link>
      <Link href="/contact" activeClassName="active">Contact</Link>
    </nav>
  );
}

3.3 编程式导航 #

jsx
import { route } from 'preact-router';

function LoginForm() {
  const handleSubmit = async (e) => {
    e.preventDefault();
    
    const success = await login(credentials);
    
    if (success) {
      route('/dashboard');  // 编程式导航
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* ... */}
    </form>
  );
}

3.4 返回上一页 #

jsx
import { route } from 'preact-router';

function BackButton() {
  const handleBack = () => {
    if (window.history.length > 1) {
      window.history.back();
    } else {
      route('/');
    }
  };

  return <button onClick={handleBack}>Go Back</button>;
}

四、路由参数 #

4.1 动态参数 #

jsx
function App() {
  return (
    <Router>
      <UserList path="/users" />
      <UserDetail path="/users/:id" />
    </Router>
  );
}

function UserDetail({ id }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetchUser(id).then(setUser);
  }, [id]);

  if (!user) return <p>Loading...</p>;

  return (
    <div>
      <h1>{user.name}</h1>
      <p>Email: {user.email}</p>
    </div>
  );
}

4.2 多个参数 #

jsx
function App() {
  return (
    <Router>
      <Post path="/posts/:category/:id" />
    </Router>
  );
}

function Post({ category, id }) {
  return (
    <div>
      <h1>Category: {category}</h1>
      <p>Post ID: {id}</p>
    </div>
  );
}

4.3 可选参数 #

jsx
function App() {
  return (
    <Router>
      <Search path="/search/:query?" />
    </Router>
  );
}

function Search({ query }) {
  return (
    <div>
      <h1>Search Results</h1>
      <p>Query: {query || 'All results'}</p>
    </div>
  );
}

4.4 查询参数 #

jsx
import { useEffect, useState } from 'preact/hooks';

function SearchPage() {
  const [query, setQuery] = useState('');

  useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    setQuery(params.get('q') || '');
  }, []);

  return (
    <div>
      <h1>Search: {query}</h1>
    </div>
  );
}

// 使用
// /search?q=preact

五、嵌套路由 #

5.1 基本嵌套 #

jsx
function App() {
  return (
    <Router>
      <Dashboard path="/dashboard">
        <Overview path="/" />
        <Analytics path="/analytics" />
        <Settings path="/settings" />
      </Dashboard>
    </Router>
  );
}

function Dashboard({ children }) {
  return (
    <div class="dashboard">
      <Sidebar />
      <div class="content">
        {children}
      </div>
    </div>
  );
}

function Overview() {
  return <h1>Overview</h1>;
}

function Analytics() {
  return <h1>Analytics</h1>;
}

function Settings() {
  return <h1>Settings</h1>;
}

5.2 带参数的嵌套路由 #

jsx
function App() {
  return (
    <Router>
      <UserLayout path="/users/:userId">
        <UserProfile path="/" />
        <UserPosts path="/posts" />
        <UserSettings path="/settings" />
      </UserLayout>
    </Router>
  );
}

function UserLayout({ userId, children }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetchUser(userId).then(setUser);
  }, [userId]);

  if (!user) return <p>Loading...</p>;

  return (
    <div class="user-layout">
      <UserHeader user={user} />
      <nav>
        <Link href={`/users/${userId}`}>Profile</Link>
        <Link href={`/users/${userId}/posts`}>Posts</Link>
        <Link href={`/users/${userId}/settings`}>Settings</Link>
      </nav>
      <div class="user-content">
        {children}
      </div>
    </div>
  );
}

六、路由事件 #

6.1 监听路由变化 #

jsx
import { useEffect } from 'preact/hooks';
import { getCurrentUrl, subscribe } from 'preact-router';

function App() {
  useEffect(() => {
    const unsubscribe = subscribe((url) => {
      console.log('Route changed to:', url);
    });

    return unsubscribe;
  }, []);

  return (
    <Router>
      {/* ... */}
    </Router>
  );
}

6.2 获取当前 URL #

jsx
import { getCurrentUrl } from 'preact-router';

function CurrentPath() {
  const [currentPath, setCurrentPath] = useState(getCurrentUrl());

  useEffect(() => {
    const unsubscribe = subscribe((url) => {
      setCurrentPath(url);
    });

    return unsubscribe;
  }, []);

  return <p>Current path: {currentPath}</p>;
}

七、路由守卫 #

7.1 认证保护 #

jsx
import { route } from 'preact-router';
import { useContext, useEffect } from 'preact/hooks';

function ProtectedRoute({ component: Component, ...props }) {
  const { user } = useContext(AuthContext);

  useEffect(() => {
    if (!user) {
      route('/login', true);  // true 表示替换历史记录
    }
  }, [user]);

  if (!user) {
    return null;
  }

  return <Component {...props} />;
}

// 使用
function App() {
  return (
    <Router>
      <Home path="/" />
      <Login path="/login" />
      <ProtectedRoute path="/dashboard" component={Dashboard} />
      <ProtectedRoute path="/settings" component={Settings} />
    </Router>
  );
}

7.2 权限检查 #

jsx
function AuthorizedRoute({ component: Component, permission, ...props }) {
  const { user } = useContext(AuthContext);

  useEffect(() => {
    if (!user || !user.permissions.includes(permission)) {
      route('/unauthorized', true);
    }
  }, [user, permission]);

  if (!user || !user.permissions.includes(permission)) {
    return null;
  }

  return <Component {...props} />;
}

八、404 处理 #

8.1 默认 404 页面 #

jsx
function App() {
  return (
    <Router>
      <Home path="/" />
      <About path="/about" />
      <NotFound default />
    </Router>
  );
}

function NotFound() {
  return (
    <div class="not-found">
      <h1>404</h1>
      <p>Page not found</p>
      <Link href="/">Go Home</Link>
    </div>
  );
}

九、最佳实践 #

9.1 路由配置 #

jsx
// routes.js
export const routes = {
  home: '/',
  about: '/about',
  contact: '/contact',
  user: (id) => `/users/${id}`,
  userPosts: (id) => `/users/${id}/posts`
};

// 使用
import { routes } from './routes';

<Link href={routes.home}>Home</Link>
<Link href={routes.user(userId)}>User Profile</Link>

9.2 懒加载路由 #

jsx
import { lazy, Suspense } from 'preact/compat';

const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));

function App() {
  return (
    <Router>
      <Home path="/" />
      <Suspense fallback={<div>Loading...</div>}>
        <Dashboard path="/dashboard" />
        <Settings path="/settings" />
      </Suspense>
    </Router>
  );
}

十、总结 #

要点 说明
Router 路由容器
path 定义路由路径
Link 导航链接
route 编程式导航
参数 :id 动态参数

核心原则:

  • 使用 Link 进行导航
  • 合理组织路由结构
  • 实现路由守卫保护
  • 处理 404 情况
最后更新:2026-03-28