Logo
无废话、光速上手 React-Router
所属分类:开发日常
阅读量:113
评论数量:0
发布时间:2024-10-24 07:09

React-Router

React Router 是一个用于 React 应用的声明式路由库。它允许开发者通过组件化的方式定义应用的路由结构,使得路由管理更加直观和可维护

安装

pnpm i react-router-dom

定义路由

定义路由有两种方式,分别是对象路由和路由组件,下面将逐步介绍这两种方式的用法

两种方式

对象路由

对象路由通过 createBrowserRouter 数组中的每一项来定义,该方式适合复杂的情况下使用

import ReactDOM from 'react-dom/client';
import { createBrowserRouter, RouterProvider } from 'react-router-dom'

function Home() {
  return (
    <>
      <h1>Home</h1>
    </>
  )
}

function Cate() {
  return (
    <>
      <h1>Cate</h1>
    </>
  )
}

// 核心代码
const router = createBrowserRouter([
  {
    // 路由访问路径
    path: "/",
    // 对应的页面组件
    element: <Home />
  },
  {
    path: "/cate",
    element: <Cate />
  }
])

const root = ReactDOM.createRoot(document.getElementById('root')!);

// 使用路由                                 
root.render(
  <RouterProvider router={router}></RouterProvider>
);

组件路由

直接通过组件来定义路由,这种方式适合简单的情况下使用

import ReactDOM from 'react-dom/client';
import { BrowserRouter, Route, Routes } from 'react-router-dom'

function Home() {
  return (
    <>
      <h1>Home</h1>
    </>
  )
}

function Cate() {
  return (
    <>
      <h1>Cate</h1>
    </>
  )
}

const root = ReactDOM.createRoot(document.getElementById('root')!);

root.render(
  <BrowserRouter>
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/cate" element={<Cate />} />
    </Routes>
  </BrowserRouter>
);

嵌套路由

当多个页面需要使用相同组件时,比如网站导航栏和底部,这两个组件通常会出现在所有页面当中,而这些公共组件我们没必要在多个页面单独引入,所以就需要用到嵌套路由

对象路由

import ReactDOM from 'react-dom/client';
import { createBrowserRouter, Outlet, RouterProvider } from 'react-router-dom'

// 布局路由
function Layout() {
  return (
    <>
      <div>这是一个通用导航栏组件</div>
    	{/* 核心:这里一定要加 Outlet 否则无法显示子路由 */}
      <Outlet />
      <div>这是一个通用底部组件</div>
    </>
  )
}

function Home() {
  return (
    <>
      <h1>Home</h1>
    </>
  )
}

function Cate() {
  return (
    <>
      <h1>Cate</h1>
    </>
  )
}

function Login() {
  return (
    <>
      <h1>Login</h1>
    </>
  )
}

const router = createBrowserRouter([
  {
    path: "/",
    element: <Layout />,
    children: [
      {
        // 定义为默认路由:当访问 / 时,会显示 Home 组件
        index: true,
        element: <Home />
      },
      {
        path: "cate",
        element: <Cate />
      }
    ]
  },
  {
    path: "/login",
    element: <Login />
  }
])

const root = ReactDOM.createRoot(document.getElementById('root')!);

root.render(
  <RouterProvider router={router}></RouterProvider>
);

组件路由

<BrowserRouter>
    <Routes>
        <Route path="/" element={<Layout />}>
          <Route index element={<Home />} />
          <Route path="cate" element={<Cate />} />
        </Route>
      
        {/* <Route element={<Layout />}>
            <Route path="/" element={<Home />} />
            <Route path="cate" element={<Cate />} />
        </Route> */}

        <Route path="/login" element={<Login />} />
    </Routes>
</BrowserRouter>

404 路由

当访问一个不存在的页面时会匹配到我们自定义的 404 路由

// 404页
function NotFound() {
  return (
    <>
      <h1>404:页面未找到</h1>
    </>
  )
}

const router = createBrowserRouter([
  ...,
  {
			path: "*",
			element: <NotFound />
  }
])
<BrowserRouter>
    <Routes>
        ...
        <Route path="*" element={<NotFound />} />
    </Routes>
</BrowserRouter>

动态路由

通常应用于通过 id 获取接口中对应的数据

const router = createBrowserRouter([
    // 单个:/cate/100
    {
      path: "/cate/:id",
      element: <Cate />
    },
    // 多个:/cate/100/200
    {
      path: "/cate/:one/:two",
      element: <Cate />
    },
  	// 可选:/cate 或 /cate/100
    {
      path: "/cate/:id?",
      element: <Cate />
    },
    // 无限:/cate/100/200/300/400/500
    {
      path: "/cate/*",
      element: <Cate />
    }
])
<BrowserRouter>
    <Router>
        <Routes>
            {/* 路由组件不支持可选方式,但可以这样实现同样的效果 */}
            <Route path="/cate" element={<Cate />} />
            <Route path="/cate/:id" element={<Cate />} />

            <Route path="/cate/:one/:two" element={<Cate />} />
            <Route path="/cate/*" element={<Cate />} />
        </Routes>
    </Router>
</BrowserRouter>

获取动态路由数据

通过 useParams() 来获取动态路由的参数数据

function Cate() {
  const params = useParams();

  return (
    <>
      <h1>Cate</h1>
      <pre>{JSON.stringify(params)}</pre>
    </>
  );
}

访问:/cate/100/200

结果:{"one":"100","two":"200"}

路由跳转

通过 Link 进行路由跳转,其实它的本质还是通过 a 标签进行跳转的

<Link to="/cate">跳转</Link>
{/* 最终会编译成 */}
<a href="/cate">跳转</a>

声明式跳转

function Layout() {
  return (
    <>
      <div>这是一个通用导航栏组件</div>
      <Outlet />
      <div>这是一个通用底部组件</div>
    </>
  )
}

function Home() {
  return (
    <>
      <h1>Home</h1>

      <Link to="/cate">跳转到分类页</Link>
      <Link to="/cate/100">跳转到分类页(带参数)</Link>
    </>
  )
}

function Cate() {
  return (
    <>
      <h1>Cate</h1>
      <Link to="/">跳转到首页</Link>
    </>
  );
}

const router = createBrowserRouter([
  {
    path: "/",
    element: <Layout />,
    children: [
      {
        index: true,
        element: <Home />
      },
      {
        path: "cate/:id?",
        element: <Cate />
      }
    ]
  }
])

编程式跳转

更多关于 useNavigate 的用法请见下方 API 目录

import { ..., useNavigate } from 'react-router-dom'

function Home() {
  const navigator = useNavigate()

  return (
    <>
      <h1>Home</h1>

      <div onClick={() => navigator("/cate")}>跳转到分类页</div>
      <button onClick={() => navigator("cate/100")}>跳转到分类页(带参数)</button>
    </>
  )
}

获取跳转参数

function Home() {
  const navigator = useNavigate()

  return (
    <>
      <h1>Home</h1>

      <Link to="/cate?title=大前端">跳转到分类页</Link>
      <button onClick={() => navigator("/cate/100?title=Java")}>跳转到分类页(带参数)</button>
    </>
  )
}

function Cate() {
  // 获取路由动态参数的数据
  const params = useParams()
  
  // 获取查询参数?后面的数据
  const [searchParams] = useSearchParams()
  console.log(searchParams); // URLSearchParams {size: 1}

  return (
    <>
      <h1>Cate</h1>
      
      <div>{JSON.stringify(params)}</div>
      {/* {id: '100'} */}

      <div>{JSON.stringify(searchParams.get("title"))}</div>
      {/* Java */}
    </>
  );
}

路由模式

各个主流框架的常用路由模式有两种,分别是 historyhash 模式, ReactRouter 分别由 createBrowerRoutercreateHashRouter 函数负责创建

路由模式url表现底层原理
historyurl/loginhistory 对象 + pushState事件
hashurl/#/login监听 hashChange 事件

路由懒加载

默认情况下像以下代码在页面被访问时尽管条件为 false , 组件也一样会被加载。而如果使用了路由懒加载,那么只有条件满足时候 或者说 组件被使用时才会被加载,这样有助于性能优化及加载速度

{false && <Article>}

假设 pages 中有 HomeCate 组件,我们可以通过 lazy 将他们定义为懒加载模式

const router = createBrowserRouter([
    {
      path: "/",
      lazy: () => import("@/pages/Home/index.tsx")
    },
    {
      path: "/cate",
      // 可以省略:index.tsx
      lazy: () => import("@/pages/Cate")
    }
])

loader

通过 loader 函数我们可以在页面 渲染之前 传递给页面指定的数据,并通过 useLoaderData 来接收数据

function Cate() {
  const loaderData = useLoaderData()
  console.log(loaderData); // Hello World

  return ...
}

const router = createBrowserRouter([
  {
    path: "/",
    element: <Layout />,
    children: [
      {
        index: true,
        element: <Home />
      },
      {
        page: "cate/:id",
        element: <Cate />,
        loader: ({params}) => {
          return "Hello World!"
        }
      }
    ]
  }
])

数据也可以是异步的,通常用于获取后端接口的数据

loader: () => {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve("Hello World!")
      }, 3000)
    })
}

还可以获取路由参数,通过参数来获取对应的数据

{
    path: "/cate/:id",
    element: <Cate />,
    loader: ({ params }) => {
        return new Promise((resolve) => {
            setTimeout(() => {
              resolve(`分类的参数:${params.id}`)
            }, 3000)
        })
    }
}

API

useLocation

该方法返回当前 location 对象,主要用于监听路由的变化执行对应的逻辑

function Home() {
  let location = useLocation();

  useEffect(() => {
    // 逻辑代码
  }, [location]);

  return ...;
}

假如访问:/cate/100?title=Java#123,那么 location 的结果如下

{"pathname":"/cate/100","search":"?title=Java","hash":"#123","state":{"data":"Hello World!"},"key":"8lu1q217"}

useNavigate

该方法主要用于编程式导航的跳转

function Home() {
  const navigator = useNavigate()

  return (
    <>
      <div onClick={() => navigator("/cate")}>跳转</div>
    </>
  )
}

replace

指定 replace: true 将替换历史记录堆栈中的当前条目,而不是添加新的条目。简单来说就是默认情况下它的值为 false 也就意味着当你从 A 页面跳转到 B 页面时,点击浏览器的回退功能可以正常回退到 A 页面。但如果将值设置为 true 则跳转到 B 页面时无法回退到 A 了,因为 A 的历史记录栈被 B 替换了

navigator("/cate", { replace: true })
<Navigate to="/cate" replace />

preventScrollReset

默认情况下该参数的值为 false,表示组件重新渲染或重新跳转进入时会自动滚到顶部,如果设置为 true 则表示保持当前位置

state

在跳转时也可以通过 state 进行传递数据,通过 useLocation 接收数据

navigator("/cate", { state: { data: "Hello World!" } })
<Navigate to="/cate" state={{ data: "Hello World!" }} />

示例:

function Home() {
  const navigator = useNavigate()

  return (
    <>
      <h1>Home</h1>
      <div onClick={() => navigator("/cate", { state: { data: "Hello World!" } })}>跳转</div>
    </>
  )
}

function Cate() {
  const location = useLocation()

  return (
    <>
      <h1>Cate</h1>
      <div>{JSON.stringify(location.state)}</div>
      {/* {"data":"Hello World!"} */}
    </>
  );
}

<Navigate> 该组件会在渲染时自动重定向到指定的路径。它是 useNavigate 的组件包装器,并接受与 props 相同的所有参数

我们可以应用于以下场景:当有 token 时显示页面,反之跳转到 login

import { Navigate } from 'react-router-dom'

const AuthRoute = ({ children }: { children: React.ReactNode }) => {
  const token = "..."

  if (token) {
    return <>{children}</>
  } else {
    return <Navigate to="/login" replace />
  }
}

export default AuthRoute

作者:宇阳

版权:此文章版权归 宇阳 所有,如有转载,请注明出处!

空状态
评论列表为空~
目录
作者头像

一直对网站开发领域很感兴趣,从小就希望有一个属于自己的网站,在17年时候成功进入站长圈,并通过各种自学,以及各种折腾,才有了你现在看到的这个网站

月亮搜索返回顶部