在这一小节,我们将学习到最重要的两部分核心内容

  • react-router路由
  • react-redux状态管理,类似于vuex
  • 这两个东西都是非常的重要的东西

react-router

与vue-router一样,react中也是有router的,接下来我们来学习看看这个到底是怎么操作的

基础的使用

基础的使用非常的简单,我们使用官方推荐的一个库,来进行路由管理

  • react-router 核心组件
  • react-router-dom 应用于浏览器端的路由库(单独使用包含了react-router的核心部分)
  • react-router-native 应用于native端的路由
  1. yarn add react-router-dom
  2. # 或者,不使用 yarn
  3. npm install react-router-dom
  1. 由于最新的react-router更新了,所以不再需要单独的配置
  • Router是所有路由组件共用的底层接口组件,它是路由规则制定的最外层的容器。
  • Route路由规则匹配,并显示当前的规则对应的组件。
  • Link路由跳转的组件

image.png
小结:如果说我们的应用程序是一座小城的话,那么Route就是一座座带有门牌号的建筑物,而Link就代表了到某个建筑物的路线。有了路线和目的地,那么就缺一位老司机了,没错Router就是这个老司机。

  1. 安装完毕之后我们来使用一下

    导入三个核心的东西,注意啊我们这里是起别名的 image.png

  2. 使用Router组件包裹整个应用

image.png

  1. 使用link作为点击跳转的(入口)

image.png

  1. 使用Route配置(出口)

image.png

注意啊,这里的这个这个path就是配置的,component就是指定匹配的时候要显示的路由,link的to和Route的path是要匹配的

image.png

常用组件的说明

  • 一个React应用中只需要使用一次,除了我们之前说的BrowserRouer我们还有一个HashRouter效果是一样的,只是后面加了一个#符号
  • Link最终变成a标签,href就变成了to的属性,to也是url里的东西,也是可以更改的,
  • Route是出口,占位符,path是一个规则,注意如果有一堆Router那么一个Link会去匹配所有的Route的paht直到找到匹配成功为止

整个流程到底是什么呢?

只要rul发生变化,整个路由都会重新匹配一次
image.png

如何实现编程式导航?

非常的简单,我们之前说过React中的prop是一个核心,如果一个组件是被Route占位显示出来的,意味着这个组件内部的prop上就有一个history属性,通过它就能完成url改变 ,实现导航

image.png
history有相关的如下的一些api

默认的路由如何展示?展示还是非常的简单

只需要使用一个path = ‘/‘就好了。/就是默认的路由,进入页面就会被匹配的路由,

精确匹配

image.png

以上都是最基础的使用步骤,当然关关是基础的使用是远远不足够我们做项目的,我们需要更多的深入学习react-router

如何实现路由的参数传递呢?

比如,你点击某一个具体的文件的时候 ,我需要你这篇文章的id那么你如何传递这个id给我呢?我又要如何获取?props.params 是一种解决方案

1、目录及 组件关系图

组件关系:
基础第四部分-router - 图10

2、源码

./index.js

  1. import React from 'react';
  2. import ReactDOM from 'react-dom';
  3. import App from './components/App/App';
  4. ReactDOM.render(<App />, document.getElementById('root'));

./src/components/App/App.js

  1. import React , { Component } from 'react';
  2. import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
  3. import About from '../About/About';
  4. class App extends Component {
  5. constructor() {
  6. super();
  7. this.state = {
  8. lists: ['10010', '10086', '8000'], // 在react中万物皆组件,万物皆组件,跟我默念,万物皆组件!!!!!
  9. };
  10. }
  11. render() {
  12. let linkList = this.state.lists.map(item => {
  13. return (
  14. <li><Link to={`/about/${item}`}> {item} </Link></li>
  15. );
  16. });
  17. return (
  18. <Router>
  19. <div>
  20. <ul> { linkList } </ul>
  21. <Route path="/about/:tel" component={About} />
  22. </div>
  23. </Router>
  24. );
  25. }
  26. }
  27. export default App;

基础第四部分-router - 图11
注:< Route>在选择渲染About组件时,其实也传给了About组件一个 match对象。在About中直接this.props.match即可访问。
这句话的意思就是,在路由跳转的时候,你传递了一个match对象在props上,这个match就是路由对象

./src/components/About/About.js

  1. import React, { Component } from 'react';
  2. class About extends Component {
  3. render() {
  4. return (
  5. <div>about: {this.props.match.params.tel}</div>
  6. );
  7. }
  8. }
  9. export default About;

基础第四部分-router - 图12
基础第四部分-router - 图13
点击8000后:
基础第四部分-router - 图14

第二种传参扥方式
使用query定义传值方式,注意:页面刷新后,参数丢失

  1. <Route path="/query" component={App} />
  2. //在Link组件中定义参数
  3. const param ={
  4. pathname:"/query",
  5. query:"参数"
  6. }
  7. <Link to={param}>List</Link>
  8. //在子组件中获取参数值
  9. this.props.location.query

第三种解决方案

以上仅仅是我们的点击的时候传递的值,在rul上可以拿到,但是如果我们要编程式导航,那么如何拿到我们的值呢?

  • 入股是history.repacle( 目标path,{})第二个就是你需要传递过去的对象,在目标path组件内通过this.props上有一个location能拿到第二个对象(你传递过去的值)

第四中解决方案(推荐)
可以有效的解决页面刷新后,参数丢失的问题

  1. linke定义

    1. let obj = {
    2. pathname: '/component',
    3. search: '?sort=name',
    4. query: { fromDashboard: 123 },
    5. state: { fromDashboard: 123 }
    6. }
    7. <Link to="{obj}">Zillow Group</Link>
  2. 接收

    1. this.props.location.search // search == "?sort=name"
    2. this.props.location.query // query == { fromDashboard: 123 }
    3. this.props.location.state // state == { fromDashboard: 123 }

    如何实现多层级的页面嵌套呢?实际上也非常的简单

    1、目录及 组件关系图

    组件关系:
    基础第四部分-router - 图15

    2、源码

    ./index.js

    1. import React from 'react';
    2. import ReactDOM from 'react-dom';
    3. import App from './components/App/App';
    4. ReactDOM.render(<App />, document.getElementById('root'));

    ./src/components/App/App.js

    1. import React from 'react';
    2. import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
    3. import Home from '../Home/Home';
    4. import About from '../About/About';
    5. import Inbox from '../Inbox/Inbox';
    6. function App() {
    7. return (
    8. <Router>
    9. <div>
    10. <ul>
    11. <li><Link to="/">Home</Link></li>
    12. <li><Link to="/about">About</Link></li>
    13. <li><Link to="/inbox">Inbox</Link></li>
    14. </ul>
    15. <hr/>
    16. <Route path="/" exact component={Home} />
    17. <Route path="/about" component={About} />
    18. <Route path="/inbox" component={Inbox} />
    19. </div>
    20. </Router>
    21. );
    22. }
    23. export default App;

基础第四部分-router - 图16
./src/components/About/About.js

  1. import React, { Component } from 'react';
  2. class About extends Component {
  3. render() {
  4. return (
  5. <div>i am about</div>
  6. );
  7. }
  8. }
  9. export default About;

./src/components/Inbox/Inbox.js

  1. import React, { Component } from 'react';
  2. import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
  3. class Inbox extends Component {
  4. render() {
  5. return (
  6. <div>
  7. <h2>Inbox title</h2>
  8. <ul>
  9. <li><Link to={`${this.props.match.url}/components`}> Components </Link></li>
  10. <li><Link to={`${this.props.match.url}/props-v-state`}> Props v. State </Link></li>
  11. </ul>
  12. <Route path={`${this.props.match.path}/:getId`} component={Topic} />
  13. <Route path={this.props.match.path} exact render={() => <h3>Please select a topic.</h3>}/>
  14. </div>
  15. );
  16. }
  17. }
  18. function Topic(props) {
  19. return <h3>{props.match.params.getId}</h3>;
  20. }
  21. export default Inbox;

基础第四部分-router - 图17基础第四部分-router - 图18

3、展示

基础第四部分-router - 图19
点击Inbox后:
基础第四部分-router - 图20
点击 Props v. State 后:
基础第四部分-router - 图21

如何把路由独立出来?(手写实现,当然你也可以使用router-config这个来搞)

目前啊,我发现现在的router耦合度太高了,如果我想要把他们我说的是router独立出来操作,那么我要如何做呢? 实际上还是有些复杂的,我们来看需求

  1. 需求,我希望

    1. import home from 'pages/Home.jsx'
    2. import record from 'pages/Record.jsx'
    3. import feedBack from 'pages/FeedBack.jsx'
    4. const router = [
    5. {
    6. path:'/record',
    7. component:record
    8. },
    9. {
    10. path:'/',
    11. component:home,
    12. redirect:'/feedBack',
    13. children:[
    14. {
    15. path:'/feedBack', ////就是这里vue的话会写成feedBack,但是react会/
    16. component:feedBack
    17. }
    18. ]
    19. },
    20. ]
    21. //如果熟悉vue的小伙伴会明白想干什么,
    22. // 主要是在/路径下生成home但是匹配到/之后会重新定义到/feedBack路径上去,
    23. //此时的home和feedBack组件是共存的,也就是常说的layout和contain是共存的,如果是vue的话很简单,很好做,但是react的路由我*****
  2. 实现的步骤如下

我们使用了router 对路由数据进行了构建,下面我只需要一个方法生成他,手写!但是没有必要,我们后续会介绍一个router结合使用的一个库,利用那个库,可以优雅的实现动态路由生成

  1. // 生成动态路由的方法
  2. function generateRoute(value){
  3. if(value.children){ // 如果有子组件
  4. return (
  5. <Switch key={value.path}>
  6. <Redirect exact from={value.path} to={value.redirect} />
  7. <Route key={value.path} path={value.path}>
  8. <value.component>
  9. {
  10. value.children.map(item=>{
  11. return generateRoute(item)
  12. })
  13. }
  14. </value.component>
  15. </Route>
  16. </Switch>
  17. )
  18. }
  19. // 如果没有子组件
  20. return <Route key={value.path} path={value.path} component={value.component} />
  21. }
  22. // 组件
  23. function app(){
  24. return (
  25. <Switch>
  26. {
  27. // 重点来了!
  28. router.map(item=>{
  29. return generateRoute(item)
  30. })
  31. }
  32. </Switch>
  33. )
  34. }
  35. // 在一系列的转化下他会生成这个东西
  36. <Switch>
  37. <Route path='/record' component={record}></Route>
  38. <Switch>
  39. <Redirect exact from='/' to='/feedBack'></Redirect>
  40. <Route path='/'>
  41. <Home>
  42. {
  43. <Route path='/feedBack' component={feedBack} />
  44. }
  45. </Home>
  46. </Route>
  47. </Switch>
  48. </Switch>
  49. //在这里我们看到了<Home>组件,这样在Home组件中我们可以通过this.props.children来获取传入的组件,类似于vue的插槽

相关技术点说明:

在上述的例子中,我们的这个发现有两个貌似没有接触过的东西Switch Redirect接下来我们来看看他们到底是干什么用的

  1. switch

字面上非常好理解,这个就是一个开关,同一个时间点内只匹配一个
标签,则其中的在路径相同的情况下,只匹配第一个,这个可以避免重复匹配;换句话说,匹配的时候如果有多个路径,只显示当前匹配的哪一个其他的不显示

  1. reditect

字面上非常好理解,重定向!,这个没有什么需要说明的,重定向!重什么地方跳到什么地方去

  1. Redirect from="messages/:id" to="/messages/:id" />

使用第三方库来实现 路由的配置抽离

在vue中,经常会有 路由拦截件的东西,在react中如何做到呢? 答案就是

react-router-config

  1. npm i react-router-dom react-router-config save
  1. react可以通过一款react-router-config插件做到和vue-router一样的使用块感
  • 搞一个home页面 ```javascript import React from ‘react’;

export default function Home(){ return (

这是主页

去about 去discover

) }

  1. - 建立一个about页面
  2. ```javascript
  3. import React from 'react';
  4. import {Link} from 'react-router-dom';
  5. import {renderRoutes} from 'react-router-config';
  6. //变量的导出用export,而不是export default
  7. export const About = (props)=>{
  8. const route = props.route
  9. return (
  10. <div>
  11. <h1>这是关于</h1>
  12. <a href="/">去home</a>
  13. <a href="/discover">去discover</a>
  14. <Link to="/about/my">关于我的</Link>
  15. <br />
  16. <Link to="/about/you">关于你的</Link>
  17. {/*使用renderRoutes方法继续渲染子路由,第二个参数可以进行传参*/}
  18. {renderRoutes(route.childrens,{user:'hello'})}
  19. </div>
  20. )
  21. }
  • 建立一个discover页面 ```javascript import React from ‘react’; import {Link} from ‘react-router-dom’; import {renderRoutes} from ‘react-router-config’;

export default function Discover(props){ const route = props.route return (

这是发现

去home
去about
发现地球
发现星星 {renderRoutes(route.childrens)}
) }

  1. - 再建立两个子页面
  2. my
  3. ```javascript
  4. import React from 'react';
  5. export default function My(props){
  6. return (
  7. <div>
  8. <h1>这是关于我的</h1>
  9. <p>{props.user}</p>
  10. </div>
  11. )
  12. }

you

  1. import React from 'react';
  2. export default function You(){
  3. return (
  4. <div>
  5. <h1>这是关于你的</h1>
  6. </div>
  7. )
  8. }
  • 我们还需要一些辅助页面

Star

  1. import React from 'react';
  2. export default function Star(){
  3. return (
  4. <div>
  5. <h1>这是发现星星</h1>
  6. </div>
  7. )
  8. }

Earth

  1. import React from 'react';
  2. //<p>{props.match.params.id}</p>
  3. export default function Earth(props){
  4. return (
  5. <div>
  6. <h1>这是发现地球</h1>
  7. </div>
  8. )
  9. }
  1. 建立配置文件

router.js

  1. src目录下新建文件夹route(名字自己随便取),然后在该目录下新建route.js文件,在route.js里进行文件的配置
  2. import Home from '../component/home'
  3. //注意非函数、非表达式等导入要用{}括住
  4. import {About} from '../component/about'
  5. import Discover from '../component/discover'
  6. import Earth from '../component/earth'
  7. import Star from '../component/star'
  8. import My from '../component/my'
  9. import You from '../component/you'
  10. const routes = [
  11. {
  12. path:'/',
  13. component: Home,
  14. exact:true
  15. },
  16. {
  17. path:'/about',
  18. component: About,
  19. childrens:[
  20. {
  21. path:'/about/my',
  22. component:My
  23. },
  24. {
  25. path:'/about/you',
  26. component:You
  27. }
  28. ]
  29. },
  30. {
  31. path:'/discover',
  32. component: Discover,
  33. childrens:[
  34. {
  35. path:'/discover/earth',
  36. component:Earth
  37. },
  38. {
  39. path:'/discover/star',
  40. component:Star
  41. }
  42. ]
  43. }
  44. ]
  45. export {routes}
  1. 最后的最后我们总算来渲染了

index,js

  1. import React from 'react';
  2. import ReactDOM from 'react-dom';
  3. import * as serviceWorker from './serviceWorker';
  4. import { BrowserRouter} from "react-router-dom";
  5. import { renderRoutes } from "react-router-config";
  6. import './index.css';
  7. import {routes} from './router/route';
  8. // 使用renderRoutes方法渲染路由
  9. ReactDOM.render(<BrowserRouter>
  10. { renderRoutes(routes) }
  11. </BrowserRouter>, document.getElementById('root'));
  12. serviceWorker.unregister();

以上就是一个非常简单的库的基础使用在里面

如何做路由守卫的拦截和权限的判断呢?

非常简单,使用router-config只需要改一些东西就好,什么?💩吧,这要手写一个鉴权 ,没事哥扛得住!

由于这里的这一块内容相对的比较难,需要学习redux之后才能读得明白,那么我们先去学习redux后续回过头开看这个实现的技术解决方案

关于路由鉴权和动态路由匹配的这个问题是非常值得一提的,因为在业务中会经常的使用到,所以啊,这个非常的重要!我们在学习完redux之后需要额外的认真学习,该需求的解决方案