Route

引入

如果我们要切换一个网页,从0加载一个新的网页,消耗时间也消耗资源,而route可以让react应用变成一个SPA,在URL变化的时候,不刷新页面,只是替换掉页面里的部分组件,从而实现更快的更新响应。

核心原理

React Router内部使用了一个叫 history 库(由 React Router 团队维护)来统一管理这些操作(还有一个老版路由这里就不做介绍了)。当你使用 <BrowserRouter> 时,它会:创建一个 history 对象(基于 window.history)。在组件树顶层通过 Context 把这个 history 对象和当前 location(URL 信息)传递下去。监听 popstate 事件(用户点后退/前进时触发),并通知 React 重新渲染匹配的组件

它的 <Link> 组件实际上会被渲染成:

 <a href="/about">关于我们</a>

但 React Router 给这个 <a> 添加了一个 点击事件处理器

 function handleClick(event) {
   event.preventDefault(); /*阻止浏览器跳转/刷新*/
   history.push('/about'); /*使用 History API 更新 URL*/
 }

代码实现

安装

 npm install react-router-dom

接入

首先我们要把整个APP用一个标签包裹,让给他拥有路由能力

 import App from './App.jsx'
 import { createRoot } from 'react-dom/client'
 import { BrowserRouter } from 'react-router-dom'
 import { StrictMode } from 'react'
 ​
 const root = createRoot(document.getElementById('root'))
 ​
 root.render(
   <StrictMode>
     <BrowserRouter>
       <App />
     </BrowserRouter>
   </StrictMode>
 )

抽象路由模块

我们要想Vue一样创建一个router文件夹,实现嵌套路由。

我们来简单创建几个组件

 src/
 │
 ├── pages/
 │   ├── Login.jsx               # 登录页,一级路由
 │   ├── Layout.jsx               # 布局页,包含导航栏,一级路由
 │   ├── Dashboard.jsx           # 看板,二级路由
 │   ├── Article.jsx             # 文章,二级路由
 │   └── NotFound.jsx             # 404页
 │
 └── router/
     └── index.jsx               # 路由配置文件

然后我们来配置一下路由

src/router/index.jsx

 import { useRoutes } from 'react-router-dom'
 ​
 // 引入组件
 // (实际项目中推荐使用 lazy load 懒加载,这里为了简单直接引入)
 import Login from '../pages/Login'
 import Layout from '../pages/Layout'
 import Dashboard from '../pages/Dashboard'
 import Article from '../pages/Article'
 import NotFound from '../pages/NotFound'
 ​
 // 定义路由表
 const routes = [
  {
     path: '/login',
     element: <Login />
  },
  {
     path: '/',
     element: <Layout />,
     children: [
      {
         /*访问'/'时自动渲染dashboard */
         index: true,
         element: <Dashboard />
      },
      {
         path: 'article',
         element: <Article />
      }
    ]
  },
  {
     /*所有没定义的路径*/
     path: '*',
     element: <NotFound />
  }
 ]
 ​
 export default function RouterView() {
   const element = useRoutes(routes)
   return element
 }

挂载路由

 import RouterView from './router'
 ​
 function App() {
   return (
     <div className="App">
       <RouterView />
     </div>
  )
 }
 ​
 export default App

嵌套路由

在React Router v6中我们需要使用<Outlet />

要实现嵌套路由,父组件必须写明子路由渲染在哪里,

src/pages/Layout.jsx

 import { Link, Outlet } from 'react-router-dom'
 function Layout() {
   return (
     <div style={{ border: '2px solid blue', padding: '20px' }}>
       <h1>我是通用布局 (Layout)</h1>
       <nav style={{ marginBottom: '20px', borderBottom: '1px solid #ccc' }}>
         <Link to="/" style={{ marginRight: '10px' }}>看板</Link>
         <Link to="/article">文章管理</Link>
         <Link to="/login" style={{ marginLeft: '20px', color: 'red' }}>退出登录</Link>
       </nav>
       <div style={{ border: '2px dashed green', padding: '10px' }}>
         <Outlet />
       </div>
     </div>
  )
 }
 ​
 export default Layout

导航和传参

看了上面的代码就要问了,ttdr,Link是什么东西,这就是一个导航

说白了就是不同URL的跳转,这里React 提供了两种方式,一种是Link,另一种是通过代码逻辑比如说useNavigate来实现(登陆成功后跳转回首页)

既然有跳转,我们上面也是在写一个文章管理,那么我要跳转到某一篇文章,我们就要对他们进行编号,这个时候我们跳转的时候就需要带着参数(id)去进行跳转,我要看ttdr写的前端相关的文章,我们就需要加上筛选条件,比如说/book?type=front。这就是我们下面要干的事情

核心函数

react router提供了三个核心钩子函数

  1. useNavigate() —— “老司机”

这是一个动作函数。它给你返回一个 Maps 函数,你调用它,页面就跳走了,它底层调用了浏览器 History API 的 pushState,把新的 URL 推进历史栈里,同时通知 React Router 更新组件。

  1. useParams() —— “身份证读取器”

专门读取 URL 路径里形如 :id 的动态部分,Router 会把当前的 URL 和你定义的 path 进行正则匹配,提取出冒号后面的变量。定义路由 path: '/article/:id',访问 /article/42useParams() 就会返回 { id: '42' }怎么跟Gin里面的c.Param(“key”)一样

  1. useSearchParams() —— “筛选器解析器”

专门读取 URL 里 ? 后面的内容,它返回一个数组 [searchParams, setSearchParams]searchParams 是一个对象(类似于 Map),你可以用 .get('page') 拿到值。这也非常像 React 的 useState,因为它还给了一个 set 函数让你修改查询参数。怎么Gin里面的c.Query(“key”)一样

看完这个似乎一切都闭环了!

代码实现

我们先来改个路由

   {
     path: '/',
     element: <Layout />,
     children: [
      {
         /*访问'/'时自动渲染dashboard */
         index: true,
         element: <Dashboard />
      },
      {
         path: 'article',
         element: <Article />
      },
      {
         path: 'article/:id',
         element: <ArticleDetail />
      }
    ]
  },

实现跳转,并且传参

Dashboard.jsx

 import { useNavigate } from "react-router-dom"
 ​
 function Dashbord() {
   const navigate =useNavigate()
   const goToArticle = (id) =>{
     navigate(`article/${id}`)
  }
   const goToArticleWithQuery = (id) =>{
     navigate(`/article/${id}?from=dashboard&highlight=true`)
  }
   return (
 <div>
       <h2>文章列表</h2>
       <ul>
         <li>
           <span>ID: 1001</span>
           <button onClick={() => goToArticle(1001)}>查看详情</button>
         </li>
         <li>
           <span>ID: 2002</span>
           <button onClick={() => goToArticleWithQuery(2002)}>
            查看详情
           </button>
         </li>
       </ul>
     </div>
  )
 }
 ​
 export default Dashbord

ArticleDetail.jsx

详细页

 import { useParams, useSearchParams } from 'react-router-dom'
 ​
 function ArticleDetail() {
   const params = useParams()
   const [searchParams] = useSearchParams()
   const searchId = searchParams.get('id')
   const source = searchParams.get('from')
   const isHighlight = searchParams.get('highlight')
 ​
   return (
     <div style={{ padding: '20px', border: '1px solid #ccc' }}>
       <button onClick={() => navigate(-1)}>返回上一页</button>
       
       <h1>文章详情页</h1>
       
       <div style={{ background: '#f0f0f0', padding: '10px' }}>
         <h3>Params:</h3>
         <p>当前文章 ID: <strong>{params.id}</strong></p>
         <p><i>可以拿着这个 ID 去向服务器请求文章的具体内容。</i></p>
       </div>
 ​
       <div style={{ background: '#e6f7ff', padding: '10px', marginTop: '10px' }}>
         <h3>Search:</h3>
         <p>来源: {source || '未知来源'}</p>
         <p>是否高亮: {isHighlight === 'true' ? '是' : '否'}</p>
       </div>
     </div>
  )
 }
 export default ArticleDetail

Params vs Search:该选谁?

特性Params (路径参数)Search (查询参数)
外观/article/1001/article?id=1001
语义资源本身 (Resource)资源的修饰 (Modifier)
必选性通常是必须的(没有ID就没法显示文章)通常是可选的(没有排序也能显示列表)
路由表需要在路由表中预定义 (path: '/:id')不需要定义,随时可以在 URL 后面加

路由别名设置

懒得按../,有没有更简单的方法,有的有的兄弟。

我们用vite 构建我们要先告诉vite怎么识别别名,我们打开vite.config.js

 import { defineConfig } from 'vite'
 import react from '@vitejs/plugin-react-swc'
 import path from 'path'
 // https://vite.dev/config/
 export default defineConfig({
   plugins: [react()],
   resolve: {
     alias: {
       '@': path.resolve(__dirname, 'src')
    }
  },
 })
 ​

vite知道了但是vscode还不知道,我们在根目录新建一个jsconfig.json

 {
   "compilerOptions": {
     "baseUrl": "./",
     "paths": {
       "@/*": ["src/*"]
    }
  }
 }
暂无评论

发送评论 编辑评论


				
上一篇