diff --git a/.vitepress/config.mjs b/.vitepress/config.mjs index 7837a64..13dcba6 100644 --- a/.vitepress/config.mjs +++ b/.vitepress/config.mjs @@ -97,7 +97,7 @@ export default defineConfig({ { text: "安装", link: "/library/installation" }, { text: "路由", link: "/library/routing" }, { text: "导航", link: "/library/navigating" }, - { text: "URL 值", link: "/library/url-values" }, + { text: "URL传值", link: "/library/url-values" }, ], }, ], @@ -176,7 +176,6 @@ export default defineConfig({ ], }, ], - socialLinks: [ { icon: "github", link: "https://github.com/gongph/react-router7-doc" }, ], diff --git a/src/library/installation.md b/src/library/installation.md index 7144b3c..104e7cb 100644 --- a/src/library/installation.md +++ b/src/library/installation.md @@ -1 +1,30 @@ # 安装 + +你可以用 Vite 脚手架工具创建一个项目,并选择 `React` 模板,除此之外你也可以用自己喜欢的工具启动程序。 + +```sh +npx create-vite@latest +``` + +接下来从 npm 安装 React Router 依赖: + +```sh +npm i react-router +``` + +最后,在你的应用程序外层渲染一个 `` 组件: + +```tsx +import React from "react"; +import ReactDOM from "react-dom/client"; +import { BrowserRouter } from "react-router"; +import App from "./app"; + +const root = document.getElementById("root"); + +ReactDOM.createRoot(root).render( + + + +); +``` diff --git a/src/library/navigating.md b/src/library/navigating.md new file mode 100644 index 0000000..cfa4ef0 --- /dev/null +++ b/src/library/navigating.md @@ -0,0 +1,117 @@ +# 导航 + +使用 ``、`` 和 `useNavigate` 来进行导航。 + +## NavLink + +该组件适用于那些需要渲染激活状态的导航链接。 + +```tsx +import { NavLink } from "react-router"; + +export function MyAppNav() { + return ( + + ); +} +``` + +当 `` 处于激活状态时,它会自动拥有一个 `.active` 类名,以便于使用 CSS 轻松设置样式: + +```css +a.active { + color: red; +} +``` + +它还在 `className`(类名)、`style`(样式)以及 `children`(子元素)上有回调属性,这些回调属性带有活动状态,可用于内联样式设置或条件渲染。 + +```tsx +// className + (isActive ? "text-red-500" : "text-black")} +> + Messages + +``` + +```tsx +// style + ({ + color: isActive ? "red" : "black", + })} +> + Messages + +``` + +```tsx +// children + + {({ isActive }) => ( + + {isActive ? "👉" : ""} Tasks + + )} + +``` + +## Link + +不需要激活样式时,可以使用 `` 组件。 + +```tsx +import { Link } from "react-router"; + +export function LoggedOutMessage() { + return ( +

+ 您已退出. 再次登录 +

+ ); +} +``` + +## useNavigate + +这个钩子(hook)允许程序在无需用户交互的情况下将用户导航至新页面。 + +对于常规导航而言,最好使用 `` 或 ``。它们能提供更好的默认用户体验,比如键盘事件、无障碍标签、“在新窗口中打开”、右键上下文菜单等等。 + +如果用户长时间停留在页面上,并且你希望他们能够导航到其他页面,那么 `useNavigate` 是一个不错的选择。例如: + +- 表单提交完成之后 +- 用户长时间没有操作退出系统 +- 有时间限制的游戏界面等等场景 + +```tsx +import { useNavigate } from "react-router"; + +export function LoginPage() { + let navigate = useNavigate(); + + return ( + <> + + { + navigate("/dashboard"); + }} + /> + + + ); +} +``` diff --git a/src/library/routing.md b/src/library/routing.md new file mode 100644 index 0000000..6d88ff1 --- /dev/null +++ b/src/library/routing.md @@ -0,0 +1,218 @@ +# 路由 + +## 配置路由 + +路由是通过渲染 `` 和 `` 组件来进行配置的,它将 path 和 对应组件关联了起来。 + +```tsx +import React from "react"; +import ReactDOM from "react-dom/client"; +import { BrowserRouter, Routes, Route } from "react-router"; +import App from "./app"; + +const root = document.getElementById("root"); + +ReactDOM.createRoot(root).render( + + + } /> + + +); +``` + +下面是稍微复杂一点的例子: + +```tsx + + } /> + } /> + + }> + } /> + } /> + + + + } /> + } /> + } /> + + +``` + +## 嵌套路由 + +路由可以被嵌套在父路由中。 + +```tsx + + }> + } /> + } /> + + +``` + +父路由的 path 会自动添加到子路由的 path 中,所以上面的例子中,会创建两个路由 `"/dashboard"` 和 `"/dashboard/settings"`。 + +子路由通过父路由组件中的 `` 渲染。 + +::: code-group + +```tsx [app/dashboard.tsx] +import { Outlet } from "react-router"; + +export default function Dashboard() { + return ( +
+

Dashboard

+ {/* 将被渲染成 */} + +
+ ); +} +``` + +::: + +## 布局路由 + +没有 path 的路由看起来会有一定的嵌套关系,但不会在路由上添加额外的路径。 + +```tsx{2,9} + + }> + } /> + } /> + + + + } /> + }> + } /> + } /> + + + + +``` + +## 索引路由 + +索引路由会在其父路由组件对应的 `` 中进行渲染(就像是默认的子路由一样)。它们通过 `index` 属性来进行配置。 + +```tsx{4,8} + + }> + {/* 访问 “/” 会被渲染到 Outlet 里 */} + } /> + + }> + {/* 访问 "/dashboard" 会被渲染到 Outlet 里 */} + } /> + } /> + + + + +``` + +请注意,索引路由不能有子路由。如果你期望实现有子路由这种行为,那你可能需要一个[布局路由](#布局路由)。 + +## 路由前缀 + +一个不带 `element` 属性的 `` 会为其子路由添加路径前缀,无须引入父布局。 + +```tsx{1} + + } /> + }> + } /> + } /> + + + +``` + +## 动态传参 + +如果路由的 path 包含 `:`,那么它会被视为动态参数,当路由被匹配时,动态参数将被解析并提供给其他路由 API 接口,比如 `useParams` 。 + +```tsx +} /> +``` + +::: code-group + +```tsx [app/team.tsx] +import { useParams } from "react-router"; + +export default function Team() { + let params = useParams(); + // params.teamId +} +``` + +::: + +在一个路由中可以有多个动态参数: + +```tsx +} /> +``` + +## 可选参数 + +在动态参数后面添加 `?` 可以使其变为可选参数。 + +```tsx +} /> +``` + +也可以在静态字符串后面加上 `?`,使其变为可选参数: + +```tsx +} /> +``` + +## 通配符 + +也被称为 `“捕获所有”` 和 `*` 路由,如果一个路由模式以 `/*` 结尾,后面的任意字符串都会匹配到,也包括其他的 `/`。 + +```tsx +} /> +``` + +```ts +let params = useParams(); +// `params["*"]` 将包含 `files/` 之后剩余的URL内容。 +let filePath = params["*"]; +``` + +你可以对 `*` 进行解构,只是必须给它赋一个新的名称。一般命名为 `splat` 。 + +```ts +let { "*": splat } = useParams(); +``` + +## 导航 + +使用 `` 和 `` 进行路由之间的跳转。 + +```tsx +import { NavLink, Link } from "react-router"; + +function Header() { + return ( + + ); +} +``` diff --git a/src/library/url-values.md b/src/library/url-values.md new file mode 100644 index 0000000..db9e3a3 --- /dev/null +++ b/src/library/url-values.md @@ -0,0 +1,59 @@ +# URL 传值 + +## 路由参数 + +路由参数从动态参数中解析。 + +```tsx +} /> +``` + +在上面的案例中,`city` 是一个路由参数,它可以从 `useParams` 钩子中获取。 + +```tsx +import { useParams } from "react-router"; + +function City() { + let { city } = useParams(); + let data = useFakeDataLibrary(`/api/v2/cities/${city}`); + // ... +} +``` + +## URL 查询参数 + +查询参数是 URL 中 `?` 后面的值。可通过 `useSearchParams` 来获取它们,该函数会返回一个 [URLSearchParams](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) 的实例。 + +```tsx +function SearchResults() { + let [searchParams] = useSearchParams(); + return ( +
+

+ You searched for {searchParams.get("q")} +

+ +
+ ); +} +``` + +## Location 对象 + +React Router 会创建一个自定义 `location` 对象,该对象包含一些有用的信息,可通过 `useLocation` 来获取这些信息。 + +```ts +function useAnalytics() { + let location = useLocation(); + useEffect(() => { + sendFakeAnalytics(location.pathname); + }, [location]); +} + +function useScrollRestoration() { + let location = useLocation(); + useEffect(() => { + fakeRestoreScroll(location.key); + }, [location]); +} +```