React 学习(五)

React路由

react-router-dom@6

react路由 image-20230625085858841

⚙️安装

npm i react-router-dom

🔧 使用

另一种routes写法,

//> App.js
import { HashRouter, Routes, Route } from 'react-router-dom'
// 导入路由组件
import Film from 'xxx.js'
import Cinema from 'yyy.js'
//....
export default function App(){
  return(
  	<HashRouter>
      <Routes>
      	{/* 匹配路径,切换路由组件 */}
    		<Route path'/film' element={<Film />} />
      	<Route path='/cinema' element={<Cinema />} />
      	<Route path='/login' element={<Login />} />
      </Routes>
    </HashRouter>
  )
}

常规写法将路由配置提出,到/src/router/router.js

//> src/router/router.js
import { Routes, Route } from 'react-router-dom'
// 导入路由组件
import Film from 'xxx.js'
import Cinema from 'yyy.js'
//....
export default MyRouter function(){
  return(
  	<Routes>
    	{/* 匹配路径,切换路由组件 */}
    		<Route path'/film' element={<Film />} />
      	<Route path='/cinema' element={<Cinema />} />
      	<Route path='/login' element={<Login />} />
    </Routes>
  )
}
//> App.js
import { HashRouter } from 'react-router-dom'
import MyRouter from '/router/router.js'
export default function App(){
  return(
  	<HashRouter>
    	{/* 渲染所需要的路由组件, 这里我们将路由组件提出 */}
      
    </HashRouter>
  )
}

* 通配符用于匹配所有路径。它可以在路由配置中的路径中使用,表示匹配任何路径。

需要注意的是,通配符应该放在路由配置中的最后,以确保其他具体的路径能够优先匹配。

路由重定向Redirect

利用useNavigate导航钩子函数,自定义重定向组件

//> /src/component/Redirect.js
import { useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
// 利用props传值
const Redirect = ({ to }) => {
  const Navigate = useNavigate()
  useEffect(()=>{
    navigate(to, { replace: true })
  })
  return null
}
export default Redirect
//> router.js
//import ...
import Redirect from '/component/Redirect.js'
export default MyRouter function(){
  return(
  	<Routes>
      	{/* 重定向到now-playing路由界面 */}
    		<Route path'/film' element={ <Redirect to='/now-playing'/> } />
      	<Route path='/now-playing' element={<NowPlaying />} />
    </Routes>
  )
}

路由嵌套

//> router.js
//import ...
import Redirect from '/component/Redirect.js'
export default MyRouter function(){
  return(
  	<Routes>
    		<Route path'/film' element={ <Film /> }>
          {/*index 匹配父级根路径 */}
      		<Route index element={<Redirect to='/film/comming-soon'/>
          {/*直接写这一级路由名字,不必加上父路径 */}
          <Route path='now-playing' />                
      	</Route>
      	<Route path='/now-playing' element={<NowPlaying />} />
    </Routes>
  )
}

在父路由组件中需要占位Outlet (类似Vue的router-view)

//> /src/views/Film.js
export default function Film (){
  return (
  	<div>
    	{/* 任意层级。。。 */}
      
      <Outlet></Outlet>
    </div>
  )
}

🧭导航

声明式导航

Link

import { Link } from 'react-router-dom'
//....
// to路由路径,编译成<a ..></a>
<Link to='/film'>电影</Link>
<Link to='/cinema'>电影院</Link>
<Link to='/center'>个人中心</Link>

NavLink (可以给点击的链接设置高亮)

NavLink组件的className属性传递了一个函数,函数参数接受了一个对象,结构出来有两个属性,其中一个为isActive

​ 如果当前路由与 NavLinkto 属性匹配,则 isActivetrue,对应的类名为 active,否则为 inactive

另一个解构出来的参数为isPending:表示当前路由是否正在进行匹配过程中。如果正在匹配过程中,isPendingtrue,否则为 false

import { NavLink } from 'react-router-dom'
// 设置高亮的css文件
import from 'highlight.css'
//....
<NavLink className={({isActive})=>isActive ? 'highlight-active' : ''} to='/film'>电影</NavLink>
<NavLink className={({isActive})=>isActive ? 'highlight-active' : ''} to='/cinema'>电影院</NavLink>
<NavLink className={({isActive})=>isActive ? 'highlight-active' : ''} to='/center'>个人中心</NavLink>
编程式导航

​ this.props.history.push() v5版本 //class类组件

利用useNavigate钩子函数,进行导航路由跳转

import { useNavigate } from 'react-router-dom'
export default function xxx (){
  const navigate = useNavigate()
  return(
  	<div>
    	<button onClick={()=>{
          {/*跳转到login页面*/}
          navigate('/login') 
        }}>点击跳转</button>
    </div>
  )
}

跳转路由传参

​ query传参,配合useSearchParams

import { useNavigate } from 'react-router-dom'
export default function FilmItem(){
  const navigate = useNavigate()
  const myid = 'test-id'
  return(
  	<div>
      <button onlClick={()=>{
          navigate(`/detail?id=${myid}`)
        }}>点击</button>
    </div>
  )
}
import { useSearchParams } from 'react-router-dom'
export default function FilmItemDetail(){
  // params 当前页面的query参数, setparams设置当前页面的参数
  const [params, setparams] = useSearchParams()
  return(
    <div>
    	<button onClick={()=>{
          console.log(params.get('id'))  {/*获取当前query参数myid */}
          console.log(params.has('id'))  {/*判断是存在 */}
          setparms({id: '19wjqdnubwhqi1'})  {/*改变当前页面query参数, 只是更新当前页面参数,需要配合navigate跳转 */}
        }}>点击</button>
    </div>
  )
}

​ 路由传参,占位,配合useParamsuseNavigate路由跳转

//> router.js
···
<Route path='/detail' element={<Detail/ >}>
<Route path='detail/:myid'element={<Detail/ >}> //占位
···
//> Detail.js
import { useParams } from 'react-router-dom'
export default function FilmItemDetail(){
  // params 当前页面的路由参数
  const params = useParams()
  const navigate = useNavigate()
  return(
    <div>
    	<button onClick={()=>{
          navigate(`/detail/1000`)
        }}>点击</button>
    </div>
  )
}

🛑路由拦截

权限验证:

//> router.js
//验证模块
const isAuth = () => {
        return localStorage.getItem('token')
}

<Route path='/center' element={isAuth() ? <Center /> : <Redirect to='/login' />} />

⚠️ 有bug,这个只会执行一次

v5 是利用 render函数重新渲染解决

render = {()=>isAuth() ? <Center /> : <Redirect to='/login' />}

🔰Final 利用插槽的形式,传入需要验证的组件

// >router.js
// 用于权限验证的组件
function AuthComponent({ children }){
  const isLogin = localStorage.getItem('token')
  // 有Token:渲染插槽组件,没有:重定向到登录页
  return isLogin ? children : <Redirect to='/login' />
}

<Route path='/center' element={<AuthComponent>
          <Center /> 
</AuthComponent>} />

路由模式

HashRouter ,打包后,放在后端静态资源目录下就能使用(监测url哈希值改变)

BrowserRouter ,需要服务器端的配置来确保在刷新页面或直接访问某个路由时,服务器能够正确地响应并返回应用的入口 HTML 文件。(浏览器history API)

Web服务器按路由寻址访问

🟢 withRouter /类组件跳转方法

hooks中,在任何由Router包裹的组件都可使用useNavigate()钩子

高阶组件,是一种用于增强或修改组件行为的设计模式。

它本质上是一个函数,接受一个组件作为参数,并return 一个新的增强后的组件

v5类组件

this.props.history.push() 跳转页面

this.props.history.match() 获取路由参数

this.props.history.location() 获取当前路由

withRouter就是一个高阶函数, 用于将 React Router 的路由信息(如 matchlocationhistory)作为 props 传递给包裹的组件。它用于在不是通过路由组件直接渲染的组件中访问路由信息。

案例

在v5组件升级到v6版本时

​ 想要进行路由导航,但是在类组件中不能直接使用useNavigate方式

​ 而此时v6版本类组件组件又没有history相关方法

​ 所以采用折中的方法,自定义withRouter组件,将v6的一些钩子函数注入到需要的类组件中

//> withRouter.js
import React from 'react'
import { useLocation, useNavigate, useParams } from 'react-router-dom'

export default function withRouter(Component) {
    return function (props) {
        const push = useNavigate()
        const match = useParams()
        const location = useLocation()
        {/* 注入新的props: history */}
        return <Component {...props} history={{ push, match, location }}/>
    }
}
//使用withRouter的类组件
import React, { Component } from 'react'
import withRouter from '../../component/withRouter'

class FilmItem extends Component {
    handleChangePage = (id) => {
        this.props.history.push(`/detail/${id}`) {/**/}
    }
    render() {
        console.log('-', this.props)
        return (
            <li onClick={() => this.handleChangePage(this.props.filmId)}>
                {this.props.name}
            </li>
        )
    }
}

{/* 调用withRouter,以增强当前类组件,实现使用路由方法 */}
export default withRouter(FilmItem)

路由懒加载

路由按需加载

利用React.lazy + React.Suspense

//封装一个懒加载模块
//根据path路径加载
const lazyLoad = (path) => {
  const Component = React.lazy(()=>import(`../views/${path}`))
  return (
  	<React.Suspense fallback={<span>加载中...</span>}>
    	<Component />
    </React.Suspense>
  )
}

使用

//> router.js
//注意path是传入的路径, 因为刚刚的模版字符串,当前的path位文件名
<Route path='/cinema' element={lazyLoad('Cinema')}></Route>

useRoutes

类似Vue路由的配置写法

export default function MyRouter() {
    const element = useRoutes([
        {
            path: '/film',
            element: lazyLoad('Film'),
            children: [
                {
                    path: 'now-playing',
                    element: <NowPlaying />
                },
                {
                    path: 'comming-soon',
                    element: <CommingSoon />
                }
            ]
        },
        {
            path: '/cinema',
            element: lazyLoad('Cinema')
        },
        {
            path: '/center',
            element: <AuthComponent>
                {
                    lazyLoad('Center')
                }
            </AuthComponent>
        }
    ])
    return element
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值