1. 程式人生 > 實用技巧 >從零開始的野路子React(2)路由與頁面跳轉

從零開始的野路子React(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