从零开始的野路子React/Node(2)路由与页面跳转

这两天主要摸索了一下React的路由做法,网上看了一些资料,但总觉得比较零散,所以自己稍微总结一下,好让自己明白一些。

 

首先,对于一个稍微复杂一点的网站来说,一般都有多个页面,通常通过点击链接或者其他操作,我们可以在页面间跳转。而跳转的过程基本上就通过路由来实现。

React的路由主要依靠react-router-dom这个库(当然,其他就算有我也不知道……)来实现。下面我们来看个简单的例子:

 

1、准备各个页面

我们假设我们需要三个页面(Home, Overview, Chapters),都在components目录下,各自的内容也非常简单。

Home是个主页,我们放一些欢迎信息:

import React from 'react';

export default function Home() {
    return ( 
        <div>
            <h1>欢迎光临,随意挑选</h1>
        </div>
    );    
}

Overview是个概览页面:

import React from 'react';

export default function Overview() {
    return ( 
        <div>
            <h1>梗概</h1>
            <div>话说天下大势,分久必合,合久必分。</div>
        </div>
    );    
}

Chapters是个章节内容页面,看起来比较复杂,其实只是根据ChapDict的内容罗列第x章,以及对应的标题:

import React from 'react';

const ChapDict = {
    1:'宴桃园豪杰三结义 斩黄巾英雄首立功', 
    2:'张翼德怒鞭督邮 何国舅谋诛宦竖', 
    3:'议温明董卓叱丁原 馈金珠李肃说吕布', 
    4:'废汉帝陈留践位 谋董贼孟德献刀', 
    5:'未完待续'};

export default function Chapters () {
    var chapnum = Object.keys(ChapDict)

    return (
        <div>
        {
            chapnum.map(
                (item, idx) => (
                    <div>
                        <h2>第{ item }章</h2>
                        <p>{ ChapDict[item] }</p>
                    </div>
                )
            ) 
        }
        </div>
    );  
};

接下来,我们把Home组件放到主文件App.js中,毕竟这是我们启动后第一眼就会看到的页面:

import React from 'react';
import './App.css';

import Home from './components/Home';

function App() {
  return (
    <div>
      <Home/>
    </div>
  );
}

export default App;

通过npm install后npm start启动一下,我们可以看到一个极为粗放的主页:

 

2、添加路由

接下来,我们需要加入路由元素了。我们把三个页面组件都导入进来,并且从react-router-dom中导入BrowserRouter(名字太长,所以之后用Router代替)和Route。

现在App.js变成了:

import React from 'react';
import './App.css';
import { BrowserRouter as Router, Route } from 'react-router-dom';

import Home from './components/Home';
import Overview from './components/Overview';
import Chapters from './components/Chapters';

function App() {
  return (
    <div>
      <Router>
          <Route path='/' component={ Home }/> 
          <Route path='/overview' component={ Overview }/>
          <Route path='/chapters' component={ Chapters }/>
      </Router> 
    </div>
  );
}

export default App;

每一条路由设置,我们都用Route来设定之,它有两个参数,分别是对应的URL地址(path)和对应调用的组件(component)。我们将其依次设定。最后把这几条路由都放在Router里面就行了。

现在我们的主页看上去并没有什么变化,但是我们输入不同的URL已经能看到不同的内容了。但是有个问题是主页的内容为什么一直会显示呢?

因为路由会逐条匹配,匹配到有对应的内容显示并继续向下再匹配。而我们的主页“/”永远会第一个被匹配到,所以永远会显示。解决的方法是:

(1)path前加上exact,这样一来,一定要URL完全匹配为“/”才会显示主页,否则不会显示;

(2)再加上Switch,Switch的作用是一旦匹配到一条就不再继续向下匹配了。

所以我们把App.js改成:

import React from 'react';
import './App.css';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

import Home from './components/Home';
import Overview from './components/Overview';
import Chapters from './components/Chapters';

function App() {
  return (
    <div>
      <Router>
        <Switch>
          <Route exact path='/' component={ Home }/> 
          <Route path='/overview' component={ Overview }/>
          <Route path='/chapters' component={ Chapters }/>
        </Switch>
      </Router> 
    </div>
  );
}

export default App;

现在一切都正常多了:

然而,还有个问题就是,如果用户输入了一个我们没有定义过的网址,比如http://localhost:3000/blahblah页面就没法正常显示,怎么办呢?我们可以通过Redirect把这些乱七八糟的输入都重定向到主页(自动跳回主页)。注意Redirect一定要放在最后!设定完from和to就大功告成了:

import React from 'react';
import './App.css';
import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-dom';

import Home from './components/Home';
import Overview from './components/Overview';
import Chapters from './components/Chapters';

function App() {
  return (
    <div>
      <Router>
        <Switch>
          <Route exact path='/' component={ Home }/> 
          <Route path='/overview' component={ Overview }/>
          <Route path='/chapters' component={ Chapters }/>
          <Redirect from='/*' to='/'/>
        </Switch>
      </Router> 
    </div>
  );
}

export default App;

 

3、来个导航栏吧

到现在为止,我们已经可以通过输入不同的网址来访问不同的页面,但我们还没有做页面跳转。不如我们加个导航栏吧。我们在components目录下新建一个Navi组件:

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

export default function Navi() {
    return ( 
        <div>
            <Link to='/'>
                <p>首页</p>
            </Link>

            <Link to='/overview'>
                <p>梗概</p>
            </Link>

            <Link to='/chapters'>
                <p>章节</p>
            </Link>
        </div>
    );    
}

Navi的内容很简单,仅仅是三个段落:首页、梗概、章节而已,但是每个段落都被包含在Link当中,Link的作用就是跳转,我们设定了参数to就是需要跳转到的页面。接下来,我们需要把导航栏也加入到App.js中:

import React from 'react';
import './App.css';
import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-dom';

import Navi from './components/Navi';
import Home from './components/Home';
import Overview from './components/Overview';
import Chapters from './components/Chapters';

function App() {
  return (
    <div>
      <Router>
        <Navi/>
        <Switch>
          <Route exact path='/' component={ Home }/> 
          <Route path='/overview' component={ Overview }/>
          <Route path='/chapters' component={ Chapters }/>
          <Redirect from='/*' to='/'/>
        </Switch>
      </Router> 
    </div>
  );
}

export default App;

由于导航栏每个页面都会出现,所以不需要添加路由,我们把它放在最前面,Switch之外就行了。再来看看:

我们点击对应的部分就可以跳转到对应了页面啦~

 

4、再增加点难度?

以上都是静态的URL,要不我们试试动态的?比如每个章节都能点击,然后可以进入到那个章节的页面怎么样?

我们先在components目录下做个Chap组件,这跟之前初体验中提到的方法一样,非常简单:

import React from 'react';

export default function Chap (props) {

    return (
        <div>
            <h2>第{ props.chap }章</h2>
            <p>{ props.title }</p>
        </div>
    );
}

props的chap属性决定显示第几章,而title属性决定显示什么标题。

我们随便加个 <Chap chap='2' title='月明星稀,乌鹊南飞'/> 试试:

红框部分就是Chap组件显示的内容。然后我们再加入一条路由通往Chap组件,这里注意Chapters对应的path前要加上exact防止提前被匹配掉:

import React from 'react';
import './App.css';
import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-dom';

import Navi from './components/Navi';
import Home from './components/Home';
import Overview from './components/Overview';
import Chapters from './components/Chapters';
import Chap from './components/Chap';

function App() {
  return (
    <div>
      <Router>
        <Navi/>
        <Switch>
          <Route exact path='/' component={ Home }/> 
          <Route path='/overview' component={ Overview }/>
          <Route exact path='/chapters' component={ Chapters }/>
          <Route path='/chapters/:chap' component={ Chap }/>
          <Redirect from='/*' to='/'/>
        </Switch>
      </Router> 
    </div>
  );
}

export default App;

好了,现在我们随便在”/chapters/”后面输个数字都会进入一个新的页面,但问题是还没有内容传入进去:

这里我们要完成两个工作,一个是在Chapters中用Link做链接跳转。另一个则是通过Link传递对应的内容到Chap组件中进行显示。我们修改一下Chapters组件,加入Link,通过占位符来跳转到动态的地址:

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

const ChapDict = {
    1:'宴桃园豪杰三结义 斩黄巾英雄首立功', 
    2:'张翼德怒鞭督邮 何国舅谋诛宦竖', 
    3:'议温明董卓叱丁原 馈金珠李肃说吕布', 
    4:'废汉帝陈留践位 谋董贼孟德献刀', 
    5:'未完待续'};

export default function Chapters () {
    var chapnum = Object.keys(ChapDict)

    return (
        <div>
        {
            chapnum.map(
                (item, idx) => (
                    <div>
                        <Link to={ `/chapters/${ item }` }>
                            <h2>第{ item }章</h2>
                        </Link>
                        <p>{ ChapDict[item] }</p>
                    </div>
                )
            ) 
        }
        </div>
    );  
};

现在每个“第x章”上都有了链接,点击即可跳转到对应的页面” http://localhost:3000/chapters/x”,但依然没有内容。

我们可以先修改一下Chap组件,我们把props.chap改为props.match.params.chap,这样可以匹配URL中的通配网址(还记得我们在Route中指定了Chap组件的path='/chapters/:chap'吗),这样chap的值就能被传入了:

第x章有了,那么对应的标题呢?难道要复制一个ChapDict过来匹配吗?这样修改起来很不方便,而且不够偷懒。

办法还是有的,我们分别修改一下Chapters组件里Link的参数,以及Chap组件中title的获取方式就行。

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

const ChapDict = {
    1:'宴桃园豪杰三结义 斩黄巾英雄首立功', 
    2:'张翼德怒鞭督邮 何国舅谋诛宦竖', 
    3:'议温明董卓叱丁原 馈金珠李肃说吕布', 
    4:'废汉帝陈留践位 谋董贼孟德献刀', 
    5:'未完待续'};

export default function Chapters () {
    var chapnum = Object.keys(ChapDict)

    return (
        <div>
        {
            chapnum.map(
                (item, idx) => (
                    <div>
                        <Link to={{ 
                            pathname:`/chapters/${ item }`, 
                            query:{ title:ChapDict[item] }
                            }}>
                        <h2>第{ item }章</h2>
                        </Link>
                        <p>{ ChapDict[item] }</p>
                    </div>
                )
            ) 
        }
        </div>
    );  
};

对于Chapters组件,我们修改了Link的to参数,它的内容变成了一个Object(类似python的dict),pathname即跳转的URL地址,而query则可以放置我们想要传递的内容。这里我们把title的内容传进去。

再修改一下Chap组件:

import React from 'react';

export default function Chap (props) {
    let title = props.location.query !== undefined ? props.location.query.title : '未完待续'

    return (
        <div>
            <h2>第{ props.match.params.chap }章</h2>
            <p>{ title }</p>
        </div>
    );
}

我们通过props.location.query.title(而不是原来的props.title)来获取title的内容。

需要注意的是,这里建立了一个临时变量title,并通过一个条件判断来获取值,这样做的原因是因为我们的ChapDict只有五个章节的内容,也就是说我们章节的URL只能支持到” http://localhost:3000/chapters/5”,如果用户输入一个” http://localhost:3000/chapters/36”怎么办?直接使用props.location.query.title的话会因为props.location.query是undefined而报错。因此我们加了一步条件判断,如果query是undefined的话,我们的标题就是“未完待续”。

  

现在所有的内容都妥了~

代码见:

https://github.com/SilenceGTX/react_basic_route

 

posted @ 2020-07-05 21:36  SilenceGTX  阅读(630)  评论(0编辑  收藏  举报