Chapter 07

SolidRouter 路由

@solidjs/router 提供声明式路由、动态参数、嵌套路由和懒加载

1. 安装与基本配置

# 安装 SolidJS 官方路由库
pnpm add @solidjs/router
// index.tsx — 在应用根部配置路由
import { render } from "solid-js/web";
import { Router, Route } from "@solidjs/router";
import App from "./App";
import Home from "./pages/Home";
import About from "./pages/About";
import NotFound from "./pages/NotFound";

render(() => (
  <Router root={App}>
    <Route path="/" component={Home} />
    <Route path="/about" component={About} />
    <Route path="*" component={NotFound} />
  </Router>
), document.getElementById("root")!);
// App.tsx — root 组件,包含全局布局和 Outlet
import { A } from "@solidjs/router";

function App(props: { children: JSX.Element }) {
  return (
    <div>
      <nav>
        <A href="/">首页</A>
        <A href="/about">关于</A>
      </nav>
      <main>{props.children}</main>
    </div>
  );
}

2. <A> — 导航链接

<A> 是 SolidRouter 提供的链接组件,比原生 <a> 标签更智能:会自动在当前路由匹配时添加 active class。

import { A } from "@solidjs/router";

function NavBar() {
  return (
    <nav>
      {/* activeClass 自定义激活 class,默认 "active" */}
      <A href="/" activeClass="nav-active" end>首页</A>
      <A href="/users" activeClass="nav-active">用户</A>
      <A href="/settings" activeClass="nav-active">设置</A>
    </nav>
  );
}
// end prop:只有精确匹配 "/" 时才激活(不包括 "/users" 等子路径)

3. 动态路由

:param 定义路由参数,通过 useParams() hook 读取:

// 路由配置
<Route path="/users/:id" component={UserDetail} />
<Route path="/posts/:year/:month/:slug" component={Post} />

// UserDetail.tsx
import { useParams } from "@solidjs/router";

function UserDetail() {
  // useParams 返回响应式代理,params.id 随 URL 变化自动更新
  const params = useParams<{ id: string }>();

  const [user] = createResource(() => params.id, fetchUser);

  return (
    <div>
      <p>User ID: {params.id}</p>
      <Show when={user()} keyed>
        {(u) => <h1>{u.name}</h1>}
      </Show>
    </div>
  );
}

4. 嵌套路由

SolidRouter 支持嵌套路由,子路由通过父路由的 children prop 渲染:

// 路由配置
<Route path="/dashboard" component={Dashboard}>
  <Route path="/" component={DashboardHome} />
  <Route path="/analytics" component={Analytics} />
  <Route path="/settings" component={DashboardSettings} />
</Route>

// Dashboard.tsx — 父路由组件,渲染子路由
function Dashboard(props: { children: JSX.Element }) {
  return (
    <div class="dashboard">
      <aside>
        <A href="/dashboard" end>概览</A>
        <A href="/dashboard/analytics">分析</A>
        <A href="/dashboard/settings">设置</A>
      </aside>
      <main>{props.children}</main>
    </div>
  );
}

5. useNavigate 与 useLocation

import { useNavigate, useLocation, useSearchParams } from "@solidjs/router";

function LoginPage() {
  const navigate = useNavigate();
  const location = useLocation();
  const [searchParams, setSearchParams] = useSearchParams();

  const handleLogin = async () => {
    await loginApi();
    // 登录后跳转到 redirect 参数指定的页面,默认首页
    const redirectTo = searchParams.redirect || "/";
    navigate(redirectTo, { replace: true });
  };

  return (
    <div>
      <p>当前路径: {location.pathname}</p>
      <p>重定向至: {searchParams.redirect}</p>
      <button onClick={handleLogin}>登录</button>
    </div>
  );
}

6. 路由懒加载

使用 lazy()<Suspense> 实现路由级别的代码分割:

import { lazy } from "solid-js";
import { Router, Route } from "@solidjs/router";
import { Suspense } from "solid-js";

// 懒加载路由组件(Vite 会将每个 lazy 模块单独打包)
const Home = lazy(() => import("./pages/Home"));
const Dashboard = lazy(() => import("./pages/Dashboard"));
const UserDetail = lazy(() => import("./pages/UserDetail"));

function AppRoutes() {
  return (
    <Router>
      {/* Suspense 包裹懒加载路由,提供加载中 UI */}
      <Suspense fallback={<div class="page-loading">页面加载中...</div>}>
        <Route path="/" component={Home} />
        <Route path="/dashboard" component={Dashboard} />
        <Route path="/users/:id" component={UserDetail} />
      </Suspense>
    </Router>
  );
}

7. 实战:多页应用完整路由

// router.tsx — 完整路由配置
import { lazy } from "solid-js";
import { Router, Route } from "@solidjs/router";
import RootLayout from "./layouts/Root";
import { useAuth } from "./contexts/auth";

const Home     = lazy(() => import("./pages/Home"));
const Login    = lazy(() => import("./pages/Login"));
const Dashboard= lazy(() => import("./pages/Dashboard"));
const Profile  = lazy(() => import("./pages/Profile"));

// 受保护路由组件
function Protected(props: { children: JSX.Element }) {
  const { user } = useAuth();
  return (
    <Show when={user()} fallback={<Navigate href="/login" />}>
      {props.children}
    </Show>
  );
}

export function AppRouter() {
  return (
    <Router root={RootLayout}>
      <Route path="/" component={Home} />
      <Route path="/login" component={Login} />
      <Route path="/dashboard" component={Dashboard} />
      <Route path="/profile/:id" component={Profile} />
      <Route path="*" component={() => <h1>404 页面未找到</h1>} />
    </Router>
  );
}

本章小结:@solidjs/router 提供声明式路由配置,<A> 自动激活样式,useParams/useNavigate/useLocation 读取路由信息,嵌套路由通过 children prop 渲染子路由,lazy() + Suspense 实现按需加载。路由库设计简洁,与 SolidJS 的响应式系统深度集成。