路由和导航
编辑此页面Solid Router 简化了 Solid 应用程序中的路由,帮助开发人员通过使用 JSX 或 props 传递的对象定义路由来管理导航和渲染。
Getting started
1. 安装路由
默认情况下不包含此包。
2. 设置 <Router>
组件
通过渲染 Router 组件来启动您的应用程序。该组件将匹配 URL 以显示所需的页面。
import { render } from "solid-js/web";import { Router } from "@solidjs/router";
render(() => <Router />, document.getElementById("root"));
3. 提供根级布局
根布局不会在页面更改时更新,是放顶级导航和 Context Providers 的理想位置。
import { render } from "solid-js/web";import { Router } from "@solidjs/router";
const App = (props) => ( <> <h1>Site Title</h1> {props.children} </>);
render(() => <Router root={App} />, document.getElementById("root"));
4. 添加路由
每个路由都使用 Route
组件添加到 Router
中。在这里,您指定一个路径和一个在用户导航到该路径后要渲染的组件。
import { render } from "solid-js/web";import { Router, Route } from "@solidjs/router";
import Home from "./pages/Home";import Users from "./pages/Users";
const App = (props) => ( <> <h1>Site Title</h1> {props.children} </>);
render( () => ( <Router root={App}> <Route path="/" component={Home} /> <Route path="/users" component={Users} /> </Router> ), document.getElementById("root"));
5. 创建 CatchAll 路由(404 页面)
catchall 路由可用于在路由器的任何嵌套级别都找不到的页面。使用 *
将检索路径的其余部分。您还可以选择添加参数名称。
import { render } from "solid-js/web";import { Router, Route } from "@solidjs/router";
import Home from "./pages/Home";import Users from "./pages/Users";import NotFound from "./pages/NotFound";
const App = (props) => ( <> <h1>Site Title</h1> {props.children} </>);
render( () => ( <Router root={App}> <Route path="/" component={Home} /> <Route path="/users" component={Users} /> <Route path="*paramName" component={NotFound} /> </Router> ), document.getElementById("root"));
6. 创建路由链接
<A>
组件提供应用程序路由的导航。或者,您可以使用原生锚标记。但是, <A>
组件提供了附加功能,包括 CSS 属性、inactiveClass
和 activeClass
。
import { render } from "solid-js/web";import { Router, Route, A } from "@solidjs/router";
import Home from "./pages/Home";import Users from "./pages/Users";import NotFound from "./pages/NotFound";
const App = (props) => ( <> <nav> <A href="/">Home</A> <A href="/users">Users</A> </nav> <h1>Site Title</h1> {props.children} </>);
render( () => ( <Router root={App}> <Route path="/" component={Home} /> <Route path="/users" component={Users} /> <Route path="*paramName" component={NotFound} /> </Router> ), document.getElementById("root"));
延迟加载路由组件
lazy
函数延迟组件的加载,直到导航到该组件。
import { lazy } from "solid-js";import { render } from "solid-js/web";import { Router, Route } from "@solidjs/router";
const Users = lazy(() => import("./pages/Users"));const Home = lazy(() => import("./pages/Home"));
const App = (props) => ( <> <h1>Site Title</h1> {props.children} </>);
render( () => ( <Router root={App}> <Route path="/users" component={Users} /> <Route path="/" component={Home} /> </Router> ), document.getElementById("root"));
动态路由
如果事先不知道路径,则可以将路径的一部分视为灵活参数。
import { lazy } from "solid-js";import { render } from "solid-js/web";import { Router, Route } from "@solidjs/router";
const Users = lazy(() => import("./pages/Users"));const User = lazy(() => import("./pages/User"));const Home = lazy(() => import("./pages/Home"));
render( () => ( <Router> <Route path="/users" component={Users} /> <Route path="/users/:id" component={User} /> <Route path="/" component={Home} /> </Router> ), document.getElementById("root"));
冒号表示 id
可以是任何字符串,只要 URL 符合该模式,<User>
组件就会显示。
然后,您可以使用 useParams 从路由组件中访问该 id。
关于动画/过渡的注意事项:
共享相同路径的路由将被视为相同路由。如果您想强制重新渲染,可以将组件包装在带 keyed
的 <Show>
组件中:
<Show when={params.something} keyed> <MyComponent></Show>
访问参数
如果您需要访问组件内的动态路由参数,则可以使用 useParams。使用 useParams
访问参数后,就可以在您的组件中使用它们:
import { useParams } from "@solidjs/router";
const User = () => { const params = useParams(); // Retrieve the dynamic route parameters // Now you can access the id parameter as params.id
return ( <p> This is the user with the id of <code>{params.id}</code> </p> );};
useParams
对于其他 Solid 原语(例如 createResource 和 createSignal )特别有用,它们可以根据路由参数创建动态行为。
import { createResource } from "solid-js";import { useParams } from "@solidjs/router";
async function fetchUser(id) { const response = await fetch( `https://jsonplaceholder.typicode.com/users/${id}` ); return response.json();}
const User = () => { const params = useParams(); const [data] = createResource(params.id, fetchUser); // Pass the id parameter to createResource
return ( <div> <Show when={!data.loading} fallback={<p>Loading...</p>}> <div> <p>Name: {data().name}</p> <p>Email: {data().email}</p> <p>Phone: {data().phone}</p> </div> </Show> </div> );};
在这个示例中,每次 id
参数更改时,都会调用 fetchUser
函数来获取新的用户数据。
验证路由
每个路径参数都可以使用 MatchFilter
进行验证。MatchFilter
不是简单的检查参数是否存在,而是允许更复杂的路由描述:
import { lazy } from "solid-js";import { render } from "solid-js/web";import { Router, Route } from "@solidjs/router";import type { SegmentValidators } from "./types";
const User = lazy(() => import("./pages/User"));
const filters: MatchFilters = { parent: ["mom", "dad"], // allow enum values id: /^\d+$/, // only allow numbers withHtmlExtension: (v: string) => v.length > 5 && v.endsWith(".html"), // only `*.html` extensions wanted};
render(() => ( <Router> <Route path="/users/:parent/:id/:withHtmlExtension" component={User} matchFilters={filters} /> </Router>), document.getElementById("root"));
在这个例子中,matchFilters
属性提供了一种根据 filters
中定义的 parent
、id
和 withHtmlExtension
校验参数的方式。如果验证失败,路由将不匹配。
在这个例子中:
/users/mom/123/contact.html
会匹配,/users/dad/123/about.html
会匹配,/users/aunt/123/contact.html
不会匹配,因为:parent
不是 'mom' 或 'dad',/users/mom/me/contact.html
不会匹配,因为:id
不是一个数字,/users/dad/123/contact
不会匹配,因为:withHtmlExtension
缺失了.html
.
可选参数
可以通过在参数名称末尾添加问号将参数指定为可选:
// Matches stories and stories/123 but not stories/123/comments<Route path="/stories/:id?" component={Stories} />
通配符路由
要匹配给定路径中的任何后代路由,您可以使用通配符标记 (*
)。这可用于表示该路径段中的任何值。
// Will match any path beginning with foo (eg. foo/, foo/a/, foo/a/b/c)<Route path="foo/*" component={Foo} />
要将通配符部分作为参数公开给组件,您可以将其命名为:
<Route path="foo/*any" component={Foo} />
通配符标记必须是路径的最后部分; foo/*any/bar
不会创建任何路由。
多路径
Routes
组件还支持使用数组定义多个路径。这可以避免在两个或多个匹配路径之间切换时重新渲染路线:
// Navigating from "/login" to "/register" will not cause the component to re-render<Route path={["login", "register"]} component={Login} />
嵌套路由
仅叶 <Route>
节点(最里面的 <Route>
组件)被赋予路由:
<Route path="/users" component={Users}> <Route path="/:id" component={User} /></Route>
以下两个路由定义都匹配相同的 URL /users/:id
并渲染相同的组件:
<Route path="/users/:id" component={User} />
<Route path="/users"> <Route path="/:id" component={User} /></Route>
如果你想让父路由有自己的路由,你必须单独指定它:
<Route path="/users" component={Users} /><Route path="/users/:id" component={User} />
// or
<Route path="/users"> <Route path="/" component={Users} /> <Route path="/:id" component={User} /></Route>
您还可以通过使用传递给路由组件的 props.children 结合嵌套一起使用。
function PageWrapper(props) { return ( <div> <h1> We love our users! </h1> {props.children} <A href="/">Back Home</A> </div> );}
<Route path="/users" component={PageWrapper}> <Route path="/" component={Users} /> <Route path="/:id" component={User} /></Route>;
路由的配置仍然相同,但是现在它们的组件将出现在声明 props.children
的父组件内。
路由也可以无限嵌套。以下示例将仅渲染路由 /layer1/layer2
,该路由将嵌套在 3 个 div 中。
<Route path="/" component={(props) => <div>Outermost layer starts here {props.children}</div>}> <Route path="layer1" component={(props) => <div>Second layer {props.children}</div>} > <Route path="layer2" component={() => <div>Innermost layer</div>} /> </Route></Route>
预加载函数
通过预加载函数,数据获取与路由加载并行开始,因此可以尽快使用。预加载函数通过在加载路线后调用或在链接悬停时立即调用来实现。
作为唯一的参数,预加载函数传递一个用于访问路由信息的对象:
import { lazy } from "solid-js";import { Route } from "@solidjs/router";
const User = lazy(() => import("./pages/users/[id].js"));
// preload functionfunction preloadUser({ params, location }) { // do preload}
然后将预加载函数传递到 <Route>
定义中:
<Route path="/users/:id" component={User} preload={loadUser} />
您可以导出与专用[route].data.js
或 [route].data.ts
文件中的路由相对应的预加载函数和数据包装器。此模式提供了一种无需加载任何其他内容即可导入数据函数的方法。
import { query } from "@solidjs/router";
export const getUser = query(async (id) => { return (await fetch(`https://swapi.tech/api/people/${id}/`)).json();}, "getUser");
export function preloadUser({ params, location, intent }) { return getUser(params.id);}
preloadUser
接收一个包含 params
、location
和 intent
的对象。
请注意,虽然最佳实践是将这些文件编写为 [id].data.js
,但您仍然可以编写 route.data.js
。
当在除“预加载”之外的任何时间调用时,预加载函数的值都会传递给页面组件。这意味着您可以初始化页面,或使用数据 API。
要防止多次 fetch 或触发重新获取发生,您可以使用 cache 函数。
import { lazy } from "solid-js";import { render } from "solid-js/web";import { Router, Route } from "@solidjs/router";import { preloadUser } from "./pages/users/[id].data.js";
const Home = lazy(() => import("./pages/Home"));const User = lazy(() => import("./pages/users/[id]"));
render( () => ( <Router> <Route path="/" component={Home} /> <Route path="/users/:id" component={User} preload={preloadUser} /> </Router> ), document.getElementById("root"));
[id].jsx
包含渲染的组件。当您将函数包装在 createAsync
中时,一旦预期的 Promise resolve,它将产生一个 signal 。
import { createAsync } from "@solidjs/router";import { getUser } from "./[id].data";
export default function Users(props) { console.log("Users.props", props); const user = createAsync(() => getUser(props.params.id)); return ( <> <h1>User</h1> <div> <pre>{JSON.stringify(user(), null, 2)}</pre> </div> </> );}
要了解有关路由 Solid 应用程序的更多信息,请访问 Solid Router 文档。