上节课我们介绍了前端路由的原理,但是我们这些概念性的理论性的对象讲了这么多,那么我们怎么来使用路由呢?这节课我们就来给大家介绍一下路由的基本应用

回顾

在开始这节课的内容之前,我们来对上节课来做一个简单的基本回顾

  • 前端路由要依赖BOM上的history对象
  • history是一个栈的结构
  • history可以pushreplace

以上就是上节课的大致内容,大家没太理解的,把那个Demo再多看一看,其实并不难,如果说对栈不理解的,大家要去补一下数据结构的知识了。那么接下来我们来讲关于路由的基本应用。

react-router

我们既然要用到react路由,那么我们就需要用到react-router但是实际上我们安装的并不是这个库,为什么这么说呢,因为react-router库有三种工作模式:

  • webweb开发场景下用的
  • nativereact native做移动端开发用的
  • any这个是哪都能用的

那么我们学的是哪个呢?我们学习的是第一个web,所以我们在安装的时候安装的是react-router-dom库。那么这个库呢是react的插件库,专门用于使用react来实现一个SPA应用,基本上react写的项目都会用到这个库。

那么我们来安装吧,执行命令npm i react-router-dom先来把库安装好。

案例实现

那么我们安装好了我们的路由库之后我们来写个案例用一下呗,既然我们都说了是应用了,那么肯定要写案例的啊,不然光靠文字来说也太无聊了,那么我们写一个什么案例呢?我们之前是不是有一个多页面应用的一个小Demo啊?那么我们就用routerreact中来实现一下这个效果呗。

功能分析

那么我们怎么实现?我们拿到一个需求是不是要先分析一下?当我们拿到这个Demo的原理图的时候第一时间要考虑的是,导航区在哪,展示区又在哪。

image-20220113130429439

大家看一下是不是这样划分我们的整个页面区域,那么上面那个标题呢?上面那个标题一直都是静态的,我们不需要去管他。

那么我们想要实现什么功能?是不是点击导航区的导航项然后去修改地址栏里的路径,然后被前端路由器监测到,我们之前一直都说路由里有一个模块会监测到路径变化,其实那个模块就是路由器。路由器监测到路径变化之后就会将对应的组件展示出来。

功能实现

那么我们的页面是单页面应用,但页面上有多个组件,那么好,拆分组件吧,我们是不是要把变化的内容来做为组件?哪里是变化的内容?是不是展示区?导航栏的内容是不是固定的?只是样式在变那放在App组件里就好了啊,那么还剩下上面的标题,这个标题我们有必要再单独弄一个组件吗?直接在App组件里面一个标题标签是不是就可以了?

好,组件拆分好了,我们现在是不是要实现静态组件?我们先来看一下静态页面的代码吧:

image-20220113131802941

我们先来对比一下这俩代码有什么区别啊?是不是就导航区的样式以及展示区的h3标签的内容有区别?那么我们展示区的组件是不是就很简单了?我就一个<h3>就行了。但是我们看静态页面中的代码里面还有class里应用样式这样行吗?那么我们直接把这些框架性的东西直接拿到App组件中是不是就行了?然后吧class改成className

// App 组件
export default class App extends Component {
  render() {
    return (
      <div>
        <div className="row">
          <div className="col-xs-offset-2 col-xs-8">
            <div className="page-header"><h2>React Router Demo</h2></div>
          </div>
        </div>
        <div className="row">
          <div className="col-xs-2 col-xs-offset-2">
            <div className="list-group">
              <a className="list-group-item" href="./about.html">About</a>
              <a className="list-group-item active" href="./home.html">Home</a>
            </div>
          </div>
          <div className="col-xs-6">
            <div className="panel">
              <div className="panel-body">
                ??????
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  }
}

我们别的先别管,先来看我们那一串问号,因为我们现在是不是还不知道展示什么?因为我们得看我们点了什么才决定展示什么啊。因为我们要由导航区来影响浏览器地址栏路径的变化,然后路由器根据路径的变化来匹配组件的展示。

但是我们现在导航区能影响路径的变化吗?不能啊,我们只是把静态网页的代码复制过来了还没,没有做任何处理呢。那么我们怎么处理?原来的代码是怎么实现的呢?是不是多页面利用<a>来做的跳转?但是我们现在要做的不是这个啊。我们怎么修改呢?

import React, { Component } from 'react';
import {Link} from 'react-router-dom';

export default class App extends Component {
  render() {
    return (
      <div>
        <div className="row">
          <div className="col-xs-offset-2 col-xs-8">
            <div className="page-header"><h2>React Router Demo</h2></div>
          </div>
        </div>
        <div className="row">
          <div className="col-xs-2 col-xs-offset-2">
            <div className="list-group">
              <Link className="list-group-item" to="/about">About</Link>
              <Link className="list-group-item" to="/home">Home</Link>
            </div>
          </div>
          <div className="col-xs-6">
            <div className="panel">
              <div className="panel-body">
                ??????
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  }
}

我们来看代码,这里我们引入了我们接触react-router的第一个组件叫Link组件,因为我们之前是<a>来做的导航栏,但是现在呢我们在react中肯定不能用<a>来实现啊,不然不就跳转了嘛,可能有人要说,之前的demo不是可能禁止<a>跳转嘛,那是不是原生js来手动实现的?我们有封装好的组件为什么要手动去实现呢?所以说我们的<a>对应react-router中的组件就是Link组件。

那么我们来看这个Link组件怎么写。首先之前的<a>className,那么这里是不是也要有?不然的话样式就没有了啊,然后<a>中有href属性,Link组件中不用href了,用to属性,这个很好理解,就是我们这个链接要去哪呗。那么我们就在to属性中写我们的路径,注意这里面的路径尽量不要大写,因为这里是不区分大小写的,其实你写大写或者小写都是一样的,我们通常都写成小写。

router初探之路由器

那么我们到这一步了,我们来看一下这个页面长什么样子吧。而且我们这么写到底能不能实现点击之后不跳转但是把地址栏路径改掉呢?

image-20220113134708450

好像不行啊,报错了。说useHref这个函数只能在<Router>组件的上下文中使用。但是我们根本就没有用这个函数啊。其实呢这个是往页面上渲染的过程中程序底层调用的这个函数,为什么会出现这个问题呢?因为我们Link组件是不允许在<Router>之外使用的,就是说在使用<Link>时必须要有<Router>包裹着,为什么呢?因为我们的所有路由都要受到路由器的统一管理。那么我们来修改一下再看一下效果吧。

image-20220113135535851

还是报错,说不能从undefined上读取属性。为什么呢?因为router有两种,一种叫做HashRouter,另一种叫做BrowserRouter。这两个有什么区别呢?BrowserRouter是由H5提出的,而HashRouter有点类似于锚点。当我们在使用Router的时候不能说我要用路由器,你得说清楚要用什么路由器,那么我们先用BrowserRouter来给大家展示一下:

image-20220113140509161

初始状态是不是什么都没有?我们来点击一下看看:

image-20220113140555231

我们点击了About然后路径变成了about,那我们再点击一下Home

image-20220113140705405

路径也变成了home,那我们这边展示区为啥是问号呢?因为我们在代码里面给写死了就是问号啊。这不是展示的错误,那么我们点击后退。

image-20220113141214332

是不是又回到了about?那么好,到这一步我们的第一步是不是打通了?路由链接来改变浏览器地址栏的路径。

router初探之监听路径

那么我们打通了第一步,我们下一步是不是就要监听路径,然后根据路径来动态展示组件?因为我们展示区已经拆成了两个组件了,那么我们要做的就是,路径是/home就展示Home组件。如果监听到的路径是/about,那就展示About组件。那么怎么办呢?

那么现在我们就要引入另外一个组件了,叫Route,因为我们有了路由链接了,也指定了路由链接将浏览器地址栏的路径修改成什么了,那么我们是不是就要监听路由,然后展示相应组件啊?那么这一个过程呢有个官方的叫法,叫注册路由。那么怎么写呢:

export default class App extends Component {
  render() {
    return (
     ...
              <div className="panel-body">
                <Route path="/about" component={About}/>
                <Route path="/home" component={Home}/>
              </div>
      ...
    );
  }
}

为了节省篇幅我就隐藏掉一部分代码了,我们看这边我写了两个<Route>,这里有两个属性,一个是路径,另一个就是组件。那么我们来看一下页面:

image-20220113142943041

又报错了说<Route>仅仅作为<Routes>的子组件,从来不会独立使用,那么我们改一下再看:

image-20220113143146053

又是之前类似的错误,useRoutes()只能在<Router>的上下文中使用,那么就在外面包一层<Router>呗。但是这里的<Router>可不是就直接用这个标签就行了啊还是要将清楚用哪个路由器的。那么我们来改一下:

export default class App extends Component {
  render() {
    return (
      ...
              <div className="panel-body">
                <BrowserRouter>
                  <Routes>
                    <Route path="/about" component={About} />
                    <Route path="/home" component={Home} />
                  </Routes>
                </BrowserRouter>
              </div>
      ...
    );
  }
}

现在我们用<BrowserRouter>包裹了<Routes>,又用<Routes>包裹了<Route>那么我们来看一下效果吧。

image-20220113143654908

刚开始是不是什么都没有?因为我们浏览器地址栏里面根本没有路径啊。那么好我们来点击

image-20220113143756744

我们点击了About但是还是什么也没展示出来,路径也变了但是为什么没展示出来呢?我们来看一下控制台:

image-20220113144937617

这么多警告,说我们的路由没有匹配到元素,也就意味着会渲染一个值为null<Outlet />组件,默认就是一个空页面。这是个啥意思呢?这是我们react-router-dom版本的问题,我们当前这个新版本中的Switch已经被Routes给替换了,而<Route>中的属性呢也呗从component换成了element那么我们来改一下再看一下效果:

image-20220113145908690

诶,我们点击了Home,为什么展示的还是About内容呢?

我们来看一下代码我们在编写路由链接的时候是不是用了<BrowserRouter>?然后在注册路由的时候是不是也用了一个<BrowserRouter>?那么这两个是同一个路由器吗?肯定不是啊,这两个路由器很明显是相互独立的啊,但是呢我们整个页面的路由其实都应该由同一个路由器去管理。那么我们应该怎么办?那么我们用一个<BrowserRouter>把我们的展示区和导航区的代码都包裹起来呢?

export default class App extends Component {
  render() {
    return (
      ...
        <BrowserRouter>
          <div className="row">
            <div className="col-xs-2 col-xs-offset-2">
              <div className="list-group">
                <Link className="list-group-item" to="/about">About</Link>
                <Link className="list-group-item" to="/home">Home</Link>
              </div>
            </div>
            <div className="col-xs-6">
              <div className="panel">
                <div className="panel-body">
                  <Routes>
                    <Route path="/about" element={<About />} />
                    <Route path="/home" element={<Home />} />
                  </Routes>
                </div>
              </div>
            </div>
          </div>
        </BrowserRouter>
      ...
    );
  }
}

这样我们的<Link><Routes>是不是都在同一个<BrowserRouter>里面?那么好我们再来看一下效果:

image-20220113150116472

image-20220113150130468

这次我们的展示就正常了,页面也没有刷新,也能切换。

但是我们有一个问题,我们这么写是不是太麻烦了?如果我们后面又要用Router,那么是不是要把<Router>的结束标签在往外扩?那么我们有没有什么一劳永逸的方法呢?

有的我们在index.js中直接把<App />括起来,这样的话是不是这个App组件都在一个Router里面?那么我们改一下看看能不能行:

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom'

import App from './App';

ReactDOM.render(
  <BrowserRouter><App /></BrowserRouter>, 
  document.getElementById('root')
);

image-20220113150732381

展示也是正常的也能切换,而且页面不刷新。

总结

  • 安装react-router-dom
  • 路由技术分两步
  • 修改浏览器地址栏路径
  • 路由器监听路径映射对应组件
  • react-router中路由器分为BrowserRouterHashRouter
  • 使用路由的话,react-router中的组件都要由路由器管理
  • 整个应用的路由都要用同一个路由器管理
  • 当前最新版的注册路由方法已经改了<Route path="" element={<Component />}>
  • 当前最新版本的SwitchRoutes代替了,当注册路由时必须要交由Routes管理

Copyright statement:The articles of this site are all original if there is no special explanation, indicate the source please when you reprint.

Link of this article:https://work.lynchow.com/article/use_react_router/