心血来潮,想学学react了,之前学过,但是用的不多,最近感觉又出了很多新的库,闲着没事来学学。具体代码可以看我的仓库
BrowserRouter
浏览器路由,这个貌似当前v6.14.2中最推荐使用的创建路由的方式,采用的是DOM的History Api。
接下来看看是怎么使用的吧。
简易示例
import * as React from "react";
import * as ReactDOM from "react-dom";
import {
createBrowserRouter,
RouterProvider,
} from "react-router-dom";
import Root, { rootLoader } from "./routes/root";
import Team, { teamLoader } from "./routes/team";
const router = createBrowserRouter([
{
path: "/",
element: <Root />,
loader: rootLoader,
children: [
{
path: "team",
element: <Team />,
loader: teamLoader,
},
],
},
]);
ReactDOM.createRoot(document.getElementById("root")).render(
<RouterProvider router={router} />
);
以上是一个简单的例子,其存在两个路由:/,/team,其中/team是/的子路由。这一部分是官方的例子,其中组件也没有具体的展示,因此看看就好了。
嵌套路由
我们创建一个文件router/index.tsx,内容如下:
import * as React from 'react';
import { createBrowserRouter, redirect } from 'react-router-dom';
import Home from '../pages/Home'
const router = createBrowserRouter([
{
path: '/',
element: <App />,
children: [
{
// 表明这个路由是一个索引路由,当路由为/时,会渲染
// 到父路由的Outlet中,旧相当于默认渲染的路由
index: true,
element: <Home />,
},
],
},
{
path: '*',
element: <Error404 />,
},
]);
export default router;
该代码中,<App/>是父路由的组件,<Home/>是子路由的组件,因此,我们想要在父路由的组件中展现子路由的组件,这时该怎么做呢?用<Outlet />:
// App.tsx
import * as React from 'react';
import { Outlet, Link, useNavigation } from 'react-router-dom';
export default function App(props) {
const navigation = useNavigation();
return (
<>
<div
style={{
width: '300px',
display: 'flex',
justifyContent: 'space-between',
marginBottom: '20px',
}}
>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/post">Post</Link>
<Link to="/post/20">Post-20</Link>
</div>
<Outlet></Outlet>
</>
);
}
// Home.tsx
import * as React from 'react';
export default function Home() {
let data = useLoaderData();
return (
<>
Home
</>
);
}
Outlet组件应该用在父路由中,用以展现子路由元素
路由参数
给path: '/',增加一个子路由:
{
path: 'post/:id?',
element: <Post />,
},
?号表明不是必须的,可以通过/post访问,也可通过/post/10访问
// Post组件
import * as React from 'react';
import { useParams } from 'react-router-dom';
export default function post() {
// 获取当前路由参数
const params = useParams();
return <>Post-{ params.id ?? '' }</>;
}
404路由
就是给一个兜底匹配,在createBrowserRouter的最后增加:
{
path: '*',
element: <Error404 />,
},
组件内容如下:
// Error404
import * as React from 'react';
export default function Error() {
return <>Error</>;
}
懒加载
对于新的react router v6,我看网上都是用的react的方式做的懒加载展示,但是作为学习呢,我们用react router v6的方式做懒加载看看。
我们可以先给App添加子路由,主要通过lazy字段,该字段主要返回一个动态加载的组件,如下:
{
path: 'about',
async lazy() {
let { Component } = await import('../pages/About');
await new Promise((resolve) => {
setTimeout(() => {
resolve(1);
}, 2000);
});
return { Component: Component };
},
},
About组件代码如下,为什么这么写呢?因为lazy字段的第一句就说明了,为了保证应用打包后更小和支持代码拆分,每个路由都可以提供一个异步函数,用于解析路由定义中不匹配的部分,包括(loader, action, Component/element, ErrorBoundary/errorElement, etc.)。
当然,用这种方式的话上面的代码就得改了,lazy字段得改为:lazy: () => import('../pages/About'),如此,该段路由会自动应用其中定义的Component,ErrorBoundary等导出的字段。
// About.tsx
import * as React from 'react';
import { isRouteErrorResponse, useRouteError } from 'react-router-dom';
export function Component() {
return (
<>
About
</>
);
}
Component.displayName = 'About';
export function ErrorBoundary() {
let error = useRouteError();
return isRouteErrorResponse(error) ? (
<h1>
{error.status} {error.statusText}
</h1>
) : (
<h1>{error.message || error}</h1>
);
}
// If you want to customize the component display name in React dev tools:
ErrorBoundary.displayName = "SampleErrorBoundary";
如此已经算是懒加载的,但当然,我们需要一个Loading组件,没有Loading就没有灵魂了,我们可以在App组件内定义:
// App.tsx
import * as React from 'react';
import { Outlet, Link, useNavigation } from 'react-router-dom';
export default function App(props) {
const navigation = useNavigation();
return (
<>
<div
style={{
width: '300px',
display: 'flex',
justifyContent: 'space-between',
marginBottom: '20px',
}}
>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/post">Post</Link>
<Link to="/post/20">Post-20</Link>
<Link to="/aaa">404</Link>
</div>
<div>加载状态:{navigation.state}</div>
<Outlet></Outlet>
</>
);
}
没错,就是通过navigation.state来判断,如果是loading,那么表示组件仍在加载中。
其他
待续...