React —— 路由的使用

Posted 旺仔好吃糖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React —— 路由的使用相关的知识,希望对你有一定的参考价值。

🧁个人主页:个人主页

✌支持我 :点赞👍收藏🌼关注🧡

文章目录

⛳React 路由

🔮路由简介

1.什么是路由

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

2.路由安装

npm install react-router-dom@5

🧩路由的使用

(1)路由方法导入

import React,  Component  from 'react'
import HashRouter,Route from 'react-router-dom'

(2)定义路由

  <HashRouter>
    <Route path='/films' component=Films/>
    <Route path='/cinemas' component=Cinemas/>
    <Route path='/center'component=Center>1111</Route>
  </HashRouter>

(3)路由重定向

💧1. 会导航到一个新的位置,新的位置将覆盖历史堆栈中的当前条目,例如服务器端重定向
💧2. 当用户访问某界面时,若该界面并不存在,则需跳转到一个我们自定义的界面,此时需要用Redirect重定向

//引入redirect
import HashRouter,Redirect,Route from 'react-router-dom'
..................................
<Redirect from='/' to="/films"></Redirect>

💧1. from: string => 要从中进行重定向的路径名

💧2. to:string => 要重定向到的位置

以上为模糊匹配,凡是以/开头的都会跳转到films

解决

引入Switch组件

import HashRouter,Redirect,Route,Switch from 'react-router-dom'
....................................
<Switch>
      <Route path='/films' component=Films/>
      <Route path='/cinemas' component=Cinemas/>
      <Route path='/center'component=Center>1
      /* 模糊匹配 凡是以/开头的都会跳转到films*/
      <Redirect from='/' to="/films"></Redirect>
</Switch>

Switch如switch语句一样,若匹配到,则不在匹配;若均未匹配到,则跳转到自定义的界面films

注意:Redirect必须放在Switch里的最后一行,表示上面路由都匹配不到,则跳转到“/films”组件

精准匹配

//语法
<Redirect from='/' to="/films" exact></Redirect>

加上 exact 表示精确匹配,只有完全是"/“时才会跳转到”/films"界面

............................................
<Switch>
   <Route path='/films' component=Films/>
   <Route path='/cinemas' component=Cinemas/>
   <Route path='/center'component=Center>1111</Route>

   /* 模糊匹配 凡是以/开头的都会跳转到films*/
   <Redirect from='/' to="/films"></Redirect>
   /* 精确匹配 */
   <Redirect from='/' to="/films" exact></Redirect>
   <Route component=NotFound></Route>
 </Switch>

当输入的路径都不匹配时,则会跳转到 NotFound界面

(4)嵌套路由

//films组件中
<Switch>
    <Route path='/films/nowplaying' component=nowplaying></Route>
    <Route path='/films/Comingsoon' component=Comingsoon></Route>

    <Redirect from="/films" to="/films/nowplaying"></Redirect>
</Switch>

(5)路由跳转方式

  • 声明式导航

    import  NavLink  from 'react-router-dom'
    <NavLink to="/films" activeClassName='active'>电影</NavLink>
    <NavLink to="/cinemas" activeClassName='active'>影院</NavLink>
    <NavLink to="/center" activeClassName='active'>我的</NavLink>
    

    点击时将会跳转到对应的界面,activeClassName属性每次加到被点击的那个标签上,可以通过这个来添加样式

  • 编程式导航(注意HOOK使用 类组件不可以直接使用)

    • 第一种写法

      //被Route包裹时,组件中会获得形参props
      this.props.history.push(`/center`)
      
    • 第二种写法

      import useHistory from 'react-router-dom'
      ......................................
      const history = useHistory()
      const handleChangePage=(id)=>
        history.push(`/detail/$id`)
      
      

(6)路由传参

  • 动态路由传参(推荐)

    //定义路由
    <Route path='/detail/:myid' component=Detail></Route>
    //传参
     history.push(`/detail/$id`)
    //获得参数
    props.match.params.myid
    

    其中 ":myid"为占位符(要传的参数),表示路由必须写成 =>/detail/111的格式

    页面刷新,参数不会丢失

  • query传参

    //定义路由
    <Route path='/detail' component=Detail></Route>
    //传参
    props.history.push(pathname:'/detail',query:myid:id)
    //获得参数
    console.log(props.location.query.myid,'利用id去后端拿数据');
    

    刷新地址栏,参数丢失

  • state传参

    //定义路由
    <Route path='/detail' component=Detail></Route>
    //传参
    history.push(pathname:'/detail',state:myid:id)
    //获得参数
    console.log(props.location.state.myid,'利用id去后端拿东西');
    

    使用HashRouter的话,刷新页面,参数会丢失

(7)路由拦截

拦截路由变化做自定义处理,尚未登录时点击观看纪录,则会跳转到登录界面

function isAuth()
    return localStorage.getItem("token")
 
............................................
<Route path='/center' render=()=>
      return isAuth()?<Center/>:<Redirect to="/login"></Redirect>
></Route>

判断token,若没有则重定向到“/login”界面

(8)路由模式

HashRouter模式

💧有 # 路径,不像后端发请求要页面

BrowserRouter模式

💧没有 # 的路径,真正向后端发请求要页面,后端没有对对应的路径处理路径,就会404

额外命名

import BrowserRouter as Router,Redirect,Route,Switch from 'react-router-dom'
......................................
<Router></Router>

将BrowserRouter/HashRouter,重新命名为Router,方便切换

(9)withRouter

作用

💧将一个组件包裹在Route里面,然后react-router的三个对象history,location,match就会被放进这个组件的props属性中,此时这个组件就有了props,并具备了路由的属性

应用场景

  <Route path='/center' render=()=>
            return isAuth()?<Center/>:<Redirect to="/login"></Redirect>
  ></Route>
  //组件使用render函数直接渲染,此时组件的this.props为空,无法执行props中的history,location,match方法

💧在层级关系组件中,无法直接获得父组件的props(除传递props外)或 父组件就没有props,此时子组件就无法具备路由的属性

语法

import React,  Component  from 'react'
import  withRouter  from 'react-router-dom'

function Center(props)
  return (
    <div>
      <div onClick=()=>
        props.history.push(`/filmsorder`)
      >
      </div>
    </div>
  )

export default withRouter(Center)

🏫项目注意

反向代理

前端解决跨域问题

  • 在src下创建 setupProxy.js文件

  • 安装

    npm i http-proxy-middleware
    
  • setupProxy.js文件中配置反向代理

    const  createProxyMiddleware  = require('http-proxy-middleware');
    module.exports = function(app) 
      // app.use(),这个可以配置多个代理
      app.use(
         //ajax是需要转发的请求(所有带有/ajax前缀的请求都会转发给 https://i.maoyan.com)
        '/ajax',
        createProxyMiddleware(
          //配置转发目标地址(能返回数据的服务器地址)
          target: 'https://i.maoyan.com',
          changeOrigin: true,
        )
      );
    // https://i.maoyan.com/ajax/mostExpected?limit=10&offset=0&token=&optimus_uuid=C43ACD00C40211EDB6FB1DC502B2262A6C9A830AE637431CBD63E72665CE4A70&optimus_risk_level=71&optimus_code=10    
    

注意:每次编写完setupProxy.js文件后,都要 npm start 重启服务器

  • 发送请求

    axios(
          url:'/ajax/mostExpected?limit=10&offset=0&token=&optimus_uuid=C43ACD00C40211EDB6FB1DC502B2262A6C9A830AE637431CBD63E72665CE4A70&optimus_risk_level=71&optimus_code=10',
          method:'get'
        ).then(res=>
          console.log(res.data);
        )
    

CSSModule

📍避免所有样式全局剩下,造成样式可能被错误覆盖

📍使用CSS Modules后,样式默认局部

📍对于class选择器、id选择器、以及加了class/id的标签选择器有效(.active ul li)

  • 将css文件名改为 => Film.module.css

  • 引入css文件

    import style from './css/Film.module.css'
    
  • <NavLink to="/films/comingsoon" activeClassName=style.zhangsan>
    ..............................
     //css文件中
     .zhangsan
     	color:green;       
     
    

如果想要切换到全局模式

  • 定义全局样式

    :global(.active)
        color:yellow;
    
    
  • 定义多个全局样式

    :global
        .link
            color:red;
        
        .box
            color:blue;
        
    
    

React 使用 React Hooks 从路由器获取道具

【中文标题】React 使用 React Hooks 从路由器获取道具【英文标题】:React get props from Router with React Hooks 【发布时间】:2019-11-05 02:49:49 【问题描述】:

我正在尝试使用 React Hooks 重构我的代码,但我真的不明白如何使用 Hooks 通过 React 路由器将 props 传递给我的组件。

旧的(正常的)React 代码如下所示:

App.js

import React from 'react';
import  withRouter  from "react-router-dom";
import Routes from './routes/Routes';

function App() 
    const childProps=something: "else";
    return (
        <div className="App">
            <Routes childProps=childProps />
        </div>
    );


export default withRouter(App);

Routes.js

import Switch, Route from 'react-router-dom';
import Game from '../game/Game';
import Scenario from '../game/Scenario';

const CustomRoute = ( component: C, props: cProps, ...rest ) =>
    <Route
        ...rest
        render=(props) =>
            <C ...props ...cProps />
        
    />;

export const Routes = (childProps) => 
    <Switch>
        <Route path="/" exact component=Game props=childProps />
        <CustomRoute path="/scenario/:id" exact component=Scenario props=childProps/>
    </Switch>

Game.js

import React from 'react';

const Game = () => 
  return (
    <div className="Game">
      <header className="Game-header">
        <a href="/scenario/0">
          START
        </a>
      </header>
    </div>
  );
;

export default Game;

Scenario.js

export default class Scenario extends Component 
    constructor(props) 
        super(props);

        this.state = 
            scenarios: null,
            scenarioId: null,
            currentScenario: null
        
    

    async componentDidMount() 
        const scenarioId = await this.props.match.params.id;
        const scenarios = await data.scenarios;
        this.setState(scenarios, scenarioId);
        this.getScenario();
    

    getScenario = () => 
        this.state.scenarios.forEach((scenario) => 
            if (scenario.id === this.state.scenarioId) 
                const currentScenario = scenario;
                this.setState(currentScenario);
            
        )
    

    render() 
        return (
            <div>
                this.state.currentScenario != null
                    ? this.state.currentScenario.options.length === 1
                        ? (
                            <div>
                                <div>this.state.currentScenario.text</div>
                                <div>this.state.currentScenario.options[0].text</div>
                                <a href="/">Go Back</a>
                            </div>
                        )
                        : (
                            <div>
                                <div>this.state.currentScenario.text</div>
                                <div>this.state.currentScenario.options.map((option, index) => (
                                    <div key=index>
                                        <a href=`/scenario/$option.to`>
                                            option.text
                                        </a>
                                    </div>
                                ))</div>
                            </div>
                        )
                    : null
                
            </div>
        );
    
;

所以我在网上找到了这段代码,它会改变我从路由器获取道具的方式:

HookRouter.js

import * as React from 'react';
import  BrowserRouter, Route  from 'react-router-dom';

const RouterContext = React.createContext(null);

export const HookedBrowserRouter = ( children ) => (
  <BrowserRouter>
    <Route>
      (routeProps) => (
        <RouterContext.Provider value=routeProps>
          children
        </RouterContext.Provider>
      )
    </Route>
  </BrowserRouter>
);

export function useRouter() 
  return React.useContext(RouterContext);
;

新的 App.js

import React from 'react';
import  withRouter  from "react-router-dom";
import Routes from './routes/Routes';
import HookedBrowserRouter, useRouter from './routes/HookRouter';

function App() 
    const childProps=something: "else";
    return (
        <HookedBrowserRouter>
        <div className="App">
            <Routes childProps=childProps />
        </div>
        </HookedBrowserRouter>
    );


export default withRouter(App);

我在新的 Scenario.js 中得到了他的帮助

import React,  Component, useState, useEffect  from 'react';
import data from '../data/fake';
import useRouter from '../routes/HookRouter';

const RouterContext = React.createContext(null);

const HookSceneario = () => 
    const [scenarios, setScenarios] = useState(null);
    const [scenarioId, setScenarioId] = useState(null);
    const [currentScenario, setCurrentScenario] = useState(null);

    // Similar to componentDidMount and componentDidUpdate:
        // Update the document title using the browser API
        // console.log(React.useContext(RouterContext));

    useEffect(() => 
        console.log(scenarios);
    );

    return (
        <div>
            // ...
        </div>
    );

所以useState 替换了类构造函数中的this.stateuseEffect 应该替换componentDidMount,但我找不到从路由器获取props 的方法。

【问题讨论】:

&lt;Scenario/&gt; 的孩子是否需要 routeProps?因为你这样做的方式,因为场景是由&lt;Route&gt; 组件呈现的,而你在render=(routeProps) =&gt; &lt;C ...routeProps)/&gt; 中传递routeProps。请注意,我已重命名为routeProps,以明确render 道具中可用的props 对象是routeProps(匹配、位置和历史记录)。因此&lt;Scenario/&gt; 已经可以访问routeProps 感谢@cbdev420 的建议。所以你是写那段代码的人。我的问题是如何将Scenario 中的这些道具称为带有 Hooks 的函数。看来我不能 console.log 任何道具,比如类。 我认为异步生命周期方法是个坏主意...我可以制作单独的异步函数并将这个逻辑移到那里。然后你在 componentdidmount 中调用这个函数,比如 getScenario @Bonjov 谢谢你的建议。除非我误解了您的评论,否则上面的代码似乎符合您的建议:在 componentDidMount 中调用异步函数。 【参考方案1】:

我认为这很好地说明了您正在尝试做的事情:

记住:

&lt;Route&gt; 渲染的组件始终可以访问routeProps(匹配、位置和历史记录)。

如果它是由 component 属性渲染的,就像在 &lt;Route ... component=Home/&gt; 中一样,这是自动的。

如果它是由render 属性渲染的,则需要传播它们,如:

// You can spread routeProps to make them available to your rendered Component
const FadingRoute = ( component: Component, ...rest ) => (
  <Route ...rest render=routeProps => (
    <FadeIn>
      <Component ...routeProps/>
    </FadeIn>
  )/>
)

Link on CodeSandbox


结果:


完整代码:

index.js

import React from "react";
import ReactDOM from "react-dom";
import  BrowserRouter as Router  from "react-router-dom";
import AllRoutes from "./AllRoutes";

function App() 
  return (
    <Router>
      <AllRoutes />
    </Router>
  );


const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

AllRoutes.js

import React from "react";
import  Switch, Route  from "react-router-dom";
import Home from "./Home";
import Component1 from "./Component1";

function AllRoutes() 
  return (
    <Switch>
      <Route exact path="/" component=Home />
      <Route exact path="/comp1" component=Component1 />
    </Switch>
  );


export default AllRoutes;

Home.js

import React from "react";
import  Link  from "react-router-dom";

function Home(props) 
  return (
    <div>
      I am HOME component
      <ul>
        <li>
          <Link to="/comp1">Component1</Link>
        </li>
      </ul>
      I have access to routeProps: YES
      <br />
      Because I'm directly rendered from a Route
      <ul>
        <li>"props.match:" + props.match.toString()</li>
        <li>"props.location:" + props.location.toString()</li>
        <li>"props.history:" + props.history.toString()</li>
      </ul>
    </div>
  );


export default Home;

Component1.js

import React from "react";
import  Link  from "react-router-dom";
import Component1Child from "./Component1Child";
import RouterContext from "./RouterContext";

function Component1(props) 
  const routeProps = 
    match: props.match,
    history: props.history,
    location: props.location
  ;

  return (
    <RouterContext.Provider value=routeProps>
      <div>
        <b>I am Component1</b>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
        </ul>
        I have access to routeProps: YES
        <br />
        Because I'm directly rendered from a Route.
        <br />
        And I automatically 'inherit' them when I'm rendered through the Route
        'component' prop
        <ul>
          <li>"props.match:" + props.match.toString()</li>
          <li>"props.location:" + props.location.toString()</li>
          <li>"props.history:" + props.history.toString()</li>
        </ul>
        <Component1Child />
      </div>
    </RouterContext.Provider>
  );


export default Component1;

Component1Child.js

import React from "react";
import Component1GrandChild from "./Component1GrandChild";

function Component1Child(props) 
  return (
    <div>
      <b>I am Component1Child</b> <br />
      <br />
      I have access to routeProps: NO
      <br />
      Because I'm NOT directly rendered from a Route.
      <br />I am rendered by Componen1 and routeProps are not automatically
      passed down.
      <ul>
        <li>"props.match:" + props.match</li>
        <li>"props.location:" + props.location</li>
        <li>"props.history:" + props.history</li>
      </ul>
      <Component1GrandChild />
    </div>
  );


export default Component1Child;

Component1GrandChild.js

import React from "react";
import useRouteProps from "./useRouteProps";

function Component1GrandChild(props) 
  const [match, location, history] = useRouteProps();
  return (
    <div>
      <b>I am Component1GrandChild</b> <br />
      <br />
      I have access to routeProps: YES
      <br />
      Because I'm consuming the routeProps provided by Component1 (which is the
      one directly rendered by the Route)
      <br /> And I'm consuming that through a custom hook called useRouteProps.
      <br />I am rendered by Componen1 and routeProps are not automatically
      passed down.
      <ul>
        <li>"props.match:" + match</li>
        <li>"props.location:" + location</li>
        <li>"props.history:" + history</li>
      </ul>
    </div>
  );


export default Component1GrandChild;

RouterContext.js

import React from "react";

const RouterContext = React.createContext(null);

export default RouterContext;

使用RouteProps.js

import  useContext  from "react";
import RouterContext from "./RouterContext";

function useRouteProps() 
  const routeProps = useContext(RouterContext);
  return [routeProps.match, routeProps.location, routeProps.history];


export default useRouteProps;

【讨论】:

非常感谢您的详细回答。请让我实现您的代码并回复您(标记为正确)。

以上是关于React —— 路由的使用的主要内容,如果未能解决你的问题,请参考以下文章

来自 react-route-dom 的 React.js 路由问题

如何通过链接和路由传递道具/状态 - react.js

一个 Next.js 路由中的两个不同子域

express.js 和 react.js cookie 保存到浏览器但不工作?

当我导航到主包未加载的嵌套路由时使用 React.js 延迟加载

在不使用路由器的情况下单击 React.js 中的按钮时如何打开新页面?