react 路由

React路由

什么是路由?

路由是根据不同的 url 地址展示不同的内容或页面。 一个针对React而设计的路由解决方案、可以友好的帮你解决React components 到 URl之间的同步映射关系。

  • 如果要使用 路由模块,第一步,安装 react-router-dom
  • 第二步,导入 路由模块

路由安装

https://reacttraining.com/react-router/web/guides/quick-start

使用React路由之前,我们需要先安装 react-router-dom这个包。比如:

npm install --save react-router-dom 

路由使用

(1) 路由方法导入

import React from 'react'
import {HashRouter,
        Route,
        Redirect,
        Switch,
        NavLink
       } from 'react-router-dom'

(2) 定义路由以及重定向

  • HashRouter :表示一个路由的跟容器,将来,所有的路由相关的东西,都要包裹在 HashRouter 里面,而且,一个网站中,只需要使用一次 HashRouter 就好了;

    • 当 使用 HashRouter 把 组件的元素包裹起来之后,网站就已经启用路由了
    • 在一个 HashRouter 中,只能有唯一的一个根元素
    • 在一个网站中,只需要使用 唯一的一次 即可
  • Route :表示一个路由规则, 在 Route 上,有两个比较重要的属性, path component

    • Route 创建的标签,就是路由规则,其中 path 表示要匹配的路由,component 表示要展示的组件
    • 在 vue 中有个 router-view 的路由标签,专门用来放置,匹配到的路由组件的,但是,在 react-router 中,并没有类似于这样的标签,而是 ,直接把 Route 标签,当作的 坑(占位符)
    • Route 具有两种身份:1. 它是一个路由匹配规则; 2. 它是 一个占位符,表示将来匹配到的组件都放到这个位置
  • Redirect:内置两个属性 from : 匹配到的路径 to :要跳转的路径

    <HashRouter>
        {props.children}
            <Switch>
                <Route path="/film" component={Film}/>
                <Route path="/cinema" component={Cinema}/>
                <Route path="/center" component={Center}/>
               {/* 动态路由 */}
            	<Route path="/detail/:id" component={Detail}/>
                <Redirect from="/" to="/film"/>
            </Switch>
    </HashRouter>
    

完整代码如下:

//配置路由-路由组件
import React from 'react'
import {HashRouter,Route,Redirect,Switch} from 'react-router-dom'
import Center from '../views/Center'
import Cinema from '../views/Cinema'
import Detail from '../views/Detail'
import Film from '../views/Film'
export default function MRouter(props) {
    return (
        <HashRouter>
            {props.children}
            <Switch>
                <Route path="/film" component={Film}/>
                <Route path="/cinema" component={Cinema}/>
                <Route path="/center" component={Center}/>
                {/* 动态路由 */}
                <Route path="/detail/:id" component={Detail}/>
                <Redirect from="/" to="/film"/>
            </Switch>
            {/* 模糊匹配 */}
        </HashRouter>
    )
}
/*
function Route(props){
    console.log(props.path)
    if(location.hash===props.path){
        return <div>
            <props.component />
        </div>
    }
    return null
}
*/

注意:

a. <Redirect from="/" to="/film"/>

b. exact 精确匹配 (Redirect 即使使用了exact, 外面还要嵌套Switch 来用)

c. Warning: Hash history cannot PUSH the same path; a new entry will not be added to the history stack,这个警告只有在hash 模式会出现。在NavLink 加上 replace 来解决.

模糊匹配与精准匹配

默认情况下,路由中的匹配规则,是模糊匹配的。上面这种匹配方式,全都匹配到了cinema组件,不会匹配到search组件。

<Route path="/cinema" component={Cinema} ></Route>
<Route path="/cinema/search" component={Search}></Route>

如果想让路由规则,进行精确匹配,可以为Route添加 exact 属性。

<Route path="/cinema" component={Cinema} exact></Route>
<Route path="/cinema/search" component={Search}></Route>

(3)嵌套路由

import React from 'react'
import { Route, Switch,Redirect } from 'react-router-dom'
import Comingsoon from './films/Comingsoon'
import Nowplaying from './films/Nowplaying'

export default function Film() {
    return (
        <div>
            <div style={{height:'200px',background:"yellow"}}>大轮播</div>

            <Switch>
                <Route path ="/film/nowplaying"  component={Nowplaying}/>
                <Route path ="/film/comingsoon"  component={Comingsoon}/>
                <Redirect from="/film" to="/film/nowplaying"/>
            </Switch>
        </div>
    )
}

(4)路由跳转方式

i. 声明式导航
  • Link :表示一个路由的链接(a链接),就好比 vue 中的 <router-link to=""></router-link>

  • NavLink (有高亮)

    • to:要跳转的路径(相当于a标签中的"herf"属性)
    • activeClassName:用来做选中样式的切换(相当于vue的 active-class )
import React from 'react'
import {NavLink} from 'react-router-dom'
import './Tabbar.css'
export default function Tabbar() {
    return (
        <div>
            <ul>
                <li>
                    <NavLink to="/film" activeClassName="active">电影</NavLink>
                </li>
                <li>
                    <NavLink to="/cinema" activeClassName="active">影院</NavLink>
                </li>
                <li>
                    <NavLink to="/center" activeClassName="active">我的</NavLink>
                </li>
            </ul>
        </div>
    )
}
/*
 function NavLink(props){
   return <a href={"#"+props.to} className={location.href===props.to?props.activeClassName:""}>
        {props.childrenm}
     </a>
 }
*/
ii. 编程式导航
  • vue 使用this.$router.push()
  • react 使用 props.history.push()
const handleChange = (id)=>{
    props.history.push(`/detail/${id}`)
}
动态路由:

用来路由跳转的过程中传递参数,例如点击列表页某条数据,跳转到详细页。

定义动态路由:

 <Route path="/detail/:id" component={Detail}/>

跳转到指定动态路由:

const handleChange = (id)=>{
    props.history.push(`/detail/${id}`)
}
<li key={item.filmId} onClick={()=>handleChange(item.filmId)}>{item.name}</li> 

如果我想在 Detail组件中显示路由的参数,可以通过 props.match.params获取路由中的参数。

  • vue: this.$route.params.id

  • react: props.match.params.id

useEffect(() => {
        console.log("获取详情id,发ajax给后端",props.match.params.id)
    }, [props.match.params.id])

<div> {this.props.match.params.id} </div>

完整代码如下:

import {useEffect,useState} from 'react'
import axios from 'axios'
export default function Nowplaying(props) {
    const [list, setlist] = useState([])
    useEffect(() => {
        axios.get("/test.json").then(res=>{
            setlist(res.data.data.films)
        })
    }, [])

    const handleChange = (id)=>{
        //编程式导航
        props.history.push(`/detail/${id}`)
    }
    return (
        <div>
             {
                list.map(item=>
                <li key={item.filmId} onClick={()=>handleChange(item.filmId)}>
                    <img src={item.poster} alt={item.name}/>
                    {item.name}</li>    
                )
            }
        </div>
    )
}

(5) 路由传参

(1)
this.props.history.push({ pathname : '/user' ,query : { day: 'Friday'} }) 
this.props.location.query.day 
(2) 
this.props.history.push({ pathname:'/user',state:{day : 'Friday' } }) 
this.props.location.state.day

(6) 路由拦截

const check = ()=>{
    return localStorage.getItem("token")
}
{/* 路由拦截  */}
{/* check 有token跳到center 没有token 重定向login */}
<Route path="/center" render={()=>check()?<Center/>:<Redirect to="/login"/>}/> 
<Route path="/login" component={Login}></Route>

(7) withRouter的应用与原理

function CinemaHeader (props) {
  return (
    <div>
      <button onClick={() => {
        console.log(props);
        props.history.push('/cinema/search')
         {/*跳转不到 因为props里面没有history*/}
      }}>地点</button>
    </div>
  )
}

export default function Cinema () {
  return (
    <div>
      <CinemaHeader></CinemaHeader>
    </div>
  )
}

props 打印{},因为 CinemaHeader的父组件(Cinema) 没有给他传属性,只有Route组件才有props属性。

解决方案有:

  1. 在父组件自己写一个属性传给子组件CinemaHeader,属性里面写一个回调函数,子组件调用回调函数
export default function Cinema (props) {
  return (
    <div>
      <CinemaHeader onLeftClick={() => {
        console.log("leftclick");
        props.history.push('/cinema/search')
      }}></CinemaHeader>
    </div>
  )
}

 function CinemaHeader (props) {
  return (
    <div>
      <button onClick={() => props.onLeftClick()}>地点</button>
    </div>
  )
}
  1. {…props}
// 父组件把Route传过来的props展开传给子组件,子组件就可以用props.history
export default function Cinema (props) {
  return (
    <div>
      <CinemaHeader {...props}></CinemaHeader>
    </div>
  )
}

 function CinemaHeader (props) {
  return (
    <div>
      <button onClick={() => { 
                  props.history.push('/cinema/search')
              }}>地点</button>
    </div>
  )
}
  1. withRouter
import {withRouter} from 'react-router-dom' // 导入 withRouter
 function CinemaHeader(props) {
    return (
        <div>
            <button onClick={()=>{
                console.log(props)
                props.history.push('/cinema/search')
            }}>北京</button>
        </div>
    )
}
export default withRouter(CinemaHeader) // withRouter 包裹子组件

原理:利用高阶组件封装出来的一个组件

/*
 withRouter 高阶函数 高阶组件 (High-order-component)  ==  HOC 
 (输入低阶组件, 得到高阶组件)
*/

 function withRouter(MyComponent){
    .....
    .....
    return function(){
        return <div>
            <MyComponent history={} ..../>
        </div>
    }
 }


/* function add(a,b){
    return a+b
}
function AAA(add){
    return function(a,b){
        return add(a*a,b*b)
    }
}
// var result = add(1,2)
// console.log(result)

var advancedAdd = AAA(add)
console.log(advancedAdd(1,2)) */

自己封装高阶组件(复用思想)

import React,{useEffect} from 'react'
function withCenter(MyComponent){
    return function(){
        useEffect(() => {
            window.onresize = ()=>{
                console.log("resize")
            }
        }, [])

        return <div style={{background:"red"}}>
            <MyComponent a="1" b="2"/>
        </div>
    }
}

 function Center(props) {
    console.log(props)
    return (
        <div>
            center
        </div>
    )
}

export default withCenter(Center)

history模式

BrowserRouter 没有#,后端找不到会报404

// 配置路由的文件
import React from 'react'
import { BrowserRouter, Route, Switch } from 'react-router-dom'
import Film from '../views/Film'
import Login from '../views/Login'

export default function MyRouter (props) {
  return (
    <div>
      <BrowserRouter>
        <Switch>
          <Route path="/film" component={Film}></Route>
          <Route path="/login" component={Login}></Route>
        </Switch>
      </BrowserRouter>
    </div>
  )
}

cssmodule

css样式 会全局影响,react没有scope ,需要把css 文件改为 filename.module.css

//导入
import style from './Film.module.css'

// 用的时候通过style.样式名
<div className={style.active}>film-header</div>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值