上节课我们介绍了关于Redirect组件的使用,从而来解决了我们之前的案例中刚打开起始页面页面时没有任何展示的情况。那么这节课我们来开始讲关于嵌套路由的相关知识,也就是我们所谓的多级路由。

回顾

在这节课我们开始介绍嵌套路由之前呢,我们先来对上节课的内容来做一个简单的回顾。

  • 借助Redirect来在路由不匹配的时候来重定向至指定路由
  • 不能随便找个位置就写Redirect,通常都会写在所有路由的最下面

以上便是上节课我们所介绍的关于Redirect组件的使用与注意事项的要点,接下来我们来开始我们这节课的内容。

什么是嵌套路由

在我们介绍嵌套路由的使用之前呢,我们肯定是要知道什么是嵌套路由的。从字面意思上来说就是路由里面还有其他的路由。就像文件夹一样,a文件夹中还有一个b文件夹,那么我们b文件夹的路径就是/a/b。嵌套路由也是如此,a路由下还有b路由,那么我们就要通过host:port/a/b来访问到b路由。

可能有人听过这么一个词叫二级路由,刚才我们所举的一个小例子就叫做二级路由,当然有二级路由肯定还会有三级路由,四级路由等等,我们也就不一一列举了,像这种我们统一称之为多级路由,而这些多级路由便是我们今天要说的嵌套路由。

案例需求

当我们介绍了多级路由的概念之后,我们就来通过一个案例来看一下嵌套路由的效果。我们依然还是之前的那个案例,HomeAbout,但是呢我们在Home里面加点新东西。

image-20220117091049668

我们让Home的展示区呢也有一个导航栏,导航栏里面两个导航项可以切换展示不同的内容。这是不是相当于在我们之前案例中的Home组件中再嵌一个相同的案例进去?

页面分析

我们先来看页面效果。之前的页面是不是没有变?结构还在,标题,然后导航区和展示区,About的页面也没变,主要就是Home页面中嵌入了新的东西,这个所谓的新的东西里面呢也有导航区和展示区,导航区为NewsMessage,然后展示区要根据导航区来变化展示内容。

那么我们在写之前的案例的时候是不是把展示区抽成了两个组件?一个是Home另一个是About?那么在这里我们是不是也要把Home中的内层的内容展示区也抽成两个组件?那么我们来问大家,这两个组件是一般组件还是路由组件?

这两个组件是不是要通过路由来管理的?既然通过路由管理,那么我们是不是就要写Route标签?Route标签中的component属性来接收我们的组件名,所以说这个组件不是我们手动渲染的,而是要通过路由来管理,那么这个就是路由组件,我们需要存放在pages文件夹中的,一般组件我们要存放在components文件夹中。

静态组件实现

那么我们已经分析了页面,也对页面做了拆分,那么我们先来实现静态组件吧

// News 组件
export default class News extends Component {
  render() {
    return (
      <ul>
        <li>news001</li>
        <li>news002</li>
        <li>news003</li>
      </ul>
    )
  }
}
// Message 组件
export default class Message extends Component {
  render() {
    return (
      <ul>
        <li><a href="/message1">message001</a></li>
        <li><a href="/message2">message002</a></li>
        <li><a href="/message/3">message003</a></li>
      </ul>
    )
  }
}

我们先来看一下这两个组件,这两个都是列表,那么我们就用<ul>来实现了。既然这两个组件已经定义好了,那么要想展示到页面上是不是就要在Home组件中来渲染?当然我们知道不能手动渲染,但是我们先手动渲染来看一下效果。

export default class Home extends Component {
  render() {
    return (
      <div>
        <h3>Home的内容</h3>
        <div>
          <ul className="nav nav-tabs">
            <li>
              <a className="list-group-item active" href="./home-news.html">News</a>
            </li>
            <li>
              <a className="list-group-item" href="./home-message.html">Message</a>
            </li>
          </ul>
          <News />
          <Message />
        </div>
      </div>
    );
  }
}

那么按照常理来说,这个时候应该会把NewsMessage组件都渲染出来。那么我们来看看是什么效果

image-20220117095551476

我们来看,确实这两个组件都渲染出来了,但是我们现在还没有动用任何关于路由的东西在里面,我们现在用的还都是静态页面里的那种形式,还是用<a>来做出了效果。

那么我们现在第一步是不是要设置路由链接?

export default class Home extends Component {
  render() {
    return (
      <div>
        <h3>Home的内容</h3>
        <div>
          <ul class="nav nav-tabs">
            <li><MyNavLink to="/news">News</MyNavLink></li>
            <li><MyNavLink to="/message">Message</MyNavLink></li>
          </ul>
          <News />
          <Message />
        </div>
      </div>
    );
  }
}

我们其他的先不管,先来看我们的这两个<MyNavLink>,这个是什么啊?这是不是我们封装的那个组件?因为我们要设置路由标签,但是又要加高亮效果,所以说是不是得用NavLink组件?但是我们有不想写那么多重复的东西,所以就有我们封装好的组件被,就传一个to属性和标签体内容。

那么我们看一下现在的效果,第一点击高亮的样式能不能出来,第二能不能修改浏览器地址栏的路径

image-20220117100507006

这就很尴尬了,无论我们怎么点击这两个导航栏,我们的路径都没有边,而且高亮也没有出现。这是为什么呢?这是我们之前Redirect的缘故。我们先把Redirect组件注释掉在看一下效果就知道什么原因了:

image-20220117100812261

路径变了。但是是修改了一级路由的路径,而不是在/home后面直接追加/news而是将/home给替换了,那么这样一来,在一级路由匹配的时候是不是就匹配不上了?既然匹配不上任何东西那么这里展示的内容就是空的,其次我们之前配置的Redirect是重定向到/home的,所以说之前无论怎么点都会跳转到Home,而样式并不是丢失了,是因为跳转到Home之后我们就是默认没有选中任何一个导航项。

另外还有一个很重要的点,为什么我们这里会去直接匹配一级路由呢?我们首先明确一点。我们NewsMessage的导航栏是不是在Home里面?我们是不是只有当先匹配了/home,才有机会去点击NewsMessage导航项?那么当我们点击了Home导航项,是不是要先注册一级路由?等我们展示出Home中的内容的时候,那么一级路由中的那部分代码是不是已经执行完了?然后我们再点击News导航项,这个时候路径确实变了,但是呢路由有一个重要的流程就是,一旦发生路径的改变,那么,路由就会从一级路由开始,按顺序逐层注册。

那么我们应该怎么处理呢?因为我们NewsMessage都是Home的子组件啊,那么我们来在to属性前加上父组件的路径

export default class Home extends Component {
  render() {
    return (
      <div>
        <h3>Home的内容</h3>
        <div>
          <ul class="nav nav-tabs">
            <li><MyNavLink to="/home/news">News</MyNavLink></li>
            <li><MyNavLink to="/home/message">Message</MyNavLink></li>
          </ul>
          <News />
          <Message />
        </div>
      </div>
    );
  }
}

我们来分析一下,我们这里的to属性是/home/*那么当我们点击任意一个导航项的时候先从一级路由匹配,那么能匹配上吗?我一级路由里面注册的是不是/home?这里的链接路径是不是/home/*?是不是遵循模糊匹配规则?那么我们来看一下效果是不是像我们想象中那样:

image-20220117102703187

这次我们的高亮效果也在,路径也没问题。正如我们想象中那样,一级路由中注册了/home,路径中是/home/news,然后模糊匹配,匹配到了Home,然后就展示了Home的内容,那么接下来我们内层嵌套的展示内容也就别写死了,来注册路由控制展示内容吧:

export default class Home extends Component {
  render() {
    return (
      <div>
        <h3>Home的内容</h3>
        <div>
          <ul className="nav nav-tabs">
            <li><MyNavLink to="/home/news">News</MyNavLink></li>
            <li><MyNavLink to="/home/message">Message</MyNavLink></li>
          </ul>
          <Switch>
            <Route path="/home/news" component={News} />
            <Route path="/home/message" component={Message} />
          </Switch>
        </div>
      </div>
    );
  }
}

我们来看一下代码,注册两个路由,所以用Switch组件包裹,然后路由中的path要写对应的完整path,然后我们来看一下结果:

image-20220117105347963

现在我们点击导航已经可以正常展示了。那么我们来分析一下我们点击了这个News都发生了什么:

  • 我们打开初始页面,首先我们要点击Home,只有Home展示出来了,才能够有机会去点News
  • 点击News,浏览器地址路径变为/home/news
  • 路由从一级开始逐层注册,首先和/home完成模糊匹配,成功展示Home内容,使Home高亮
  • 一级路由注册完毕开始注册二级路由,与/home/news完成匹配,展示News组件内容,并将News高亮

这就是我们成功展示News组件的完整流程。

那么我们再来思考一个问题,我们在一级路由中注册/home时,如果开启了严格匹配会发生什么?我们在匹配一级路由的时候,是不是就会什么都匹配不上?然后被重定向走了,那么有人要问了,我们一级不匹配,那么二级呢?二级是完美匹配的才对啊,但是你想啊,那一级都不匹配了还会继续去看二级吗?就好像你去吃牛肉面,老板都告诉你店里不卖牛肉面了,你还会去跟老板说牛肉面里不放香菜吗?那扭就肯定就吃点别的了啊。根本不会在往下进行了。

总结

  • 不要轻易开启严格匹配,否则可能会无法匹配子级路由
  • 嵌套路由就是多级路由,刻在某一路由下再包含其他路由
  • 嵌套路由的路径要加上父路由的路径
  • 路由匹配是按照注册顺序逐层注册匹配的

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/nested_route/