数据模式
- createBrowserRouter()
- RouterProvider
- loader
- useLoaderData()
- action
- useActionData()
- Form
配置路由
这是个简单的示例:
router.ts
ts
import { createBrowserRouter } from "react-router";
import Home from './pages/...'
...
const router = createBrowserRouter([
{
path: "/",
Component: Layout,
children: [
{ index: true, Component: Home },
{ path: "about", Component: About },
{
path: "auth",
Component: AuthLayout,
children: [
{ path: "login", Component: Login },
{ path: "register", Component: Register },
],
},
],
},
]);
export default routerApp.tsx
ts
import { RouterProvider } from 'react-router'
import router from './router'
function App() {
return (
<>
<RouterProvider router={router}/>
</>
)
}路由对象
在路由配置中还可以做数据加载操作
ts
import { createBrowserRouter, useLoaderData } from "react-router";
createBrowserRouter([
{
path: "/teams/:teamId",
loader: async ({ params }) => {
let team = await fetchTeam(params.teamId);
return { name: team.name };
},
Component: Team,
},
]);
function Team() {
let data = useLoaderData();
return <h1>{data.name}</h1>;
}- loader: 该属性可以返回一个Promise,当Promise resolve时,组件会重新渲染,并且将resolve的值作为useLoaderData的返回值。
- useLoaderData: 用于接收返回值
嵌套路由
路由可以通过 children 嵌套在父路由内。
ts
createBrowserRouter([
{
path: '/dashboard',
Component: Dashboard,
children: [
{ index: true, Component: Home },
{ path: 'settings', Component: Settings }
]
}
])子路由通过父路由中的 <Outlet /> 进行渲染
ts
import { Outlet } from 'react-router'
function Dashboard() {
return (
<>
<h1>Dashboard</h1>
<Outlet />
</>
)
}middleware
这是一个在导航前后顺序执行的中间件,它提供了一个统一的地方来执行诸如 日志记录、身份验证 之类的操作。
next 函数会继续向下执行链,也就是说,在这里可以调用 next() 来继续执行下一个中间件。
ts
createBrowserRouter([
{
path: '/',
middleware: [loggingMiddleware],
loader: rootLoader,
Commponent: Root,
children: [{
path: 'auth',
middleware: [authMiddleware],
loader: authLoader,
Component: Auth,
children: [...]
}]
}
])
async function loggingMiddleware({ request }, next) {
// 前置操作
let url = new URL(request.url);
console.log(`Starting navigation: ${url.pathname}${url.search}`);
const start = performance.now();
// 进入下一个中间件
await next();
// 后置操作
const duration = performance.now() - start;
console.log(`Navigation completed in ${duration}ms`);
}
const userContext = unstable_createContext<User>();
async function authMiddleware ({ context }) {
const userId = getUserId();
if (!userId) {
throw redirect("/login");
}
context.set(userContext, await getUserById(userId));
};当访问 /auth 是执行顺序是
loggingMiddleware
↓
next()
↓
authMiddleware
↓
next()
↓
rootLoader + authLoader
↓
渲染 Root + Auth
↓
执行 authMiddleware next() 后的代码
↓
执行 loggingMiddleware next() 后的代码lazy(懒加载)
大多数属性都可以懒加载导入,以减少初始包的大小。
ts
createBrowserRouter([
{
path: "/app",
lazy: async () => {
const {default: Component} = await import('./pages/App/AppPage')
return { Component }
},
},
]);除了Component,其他属性也可以通过这种方式
ts
createBrowserRouter([
{
path: "/app",
lazy: async () => {
const [{ default: Component }, { default: loader }] = await Promise.all([
import("./pages/App/AppPage"),
import("./app-loader"),
]);
return { Component, loader };
},
},
]);如果组件或方法是通过 export 而不是 export default 导出那么写法可以更简洁
注意:这种方式组件名必须为
Component
./pages/Login:
ts
export function Component() {
return (
<div>
<h1>Login</h1>
<input />
<button>登录</button>
</div>
);
}router:
ts
{
path: "/login",
lazy: () => import("./pages/Login"),
}