[React Note] — React 初次見面之 React Router 淺入淺出

Introduction & 前言

React Router

初次入坑 React,首要任務為了解 React Router 所以就有這篇筆記的誕生,這篇文章會講解如何簡單運用及巢狀路由的運用。

因為最近工作需求,開始會接觸到 React,剛好先透過 React Router 來了解 React,如果你也剛好正想入門或是了解一下 React 的路由跟 Vue 有什麼不一樣,可以參考本篇紀錄。


Summary & 摘要

首先要先了解到 React Router 的基礎用法,可能會合印象中 Vue Router 就透過一個 <router-view></router-view> 即可渲染有點差異,所以這邊會分四個部分(第一部分為簡單介紹)。

  1. 關於 React Router 及 React Router Dom

  2. 基礎的路由運用

  3. 巢狀的路由運用

  4. 透過 React Router Config 統一管理及渲染畫面

最後也會將範例檔放在 Github 上,有興趣的朋友都可以下載來玩看看,但需要事先提醒,因為目前筆者對 React 不是那麼熟悉,所以可能如樣式、檔案擺放或程式碼內容可能會有些許錯誤,如果有誤還請各路大神不見晾在下方留言。


關於 React Router 及 React Router Dom

React Router Dom

如果您是後來才接觸,可能會滿疑惑為什麼網路上收尋 React Router,安裝後卻出現 React Router Dom 呢?其實 React Router Dom 就只是基於 React Route 更進階的套件,核心都還是 React Route,相同地,react-router-native 也是基於 React Route 更進階的套件。

這邊我們需要知道 react-router 一些基礎用法及 react-router-dom 提供的用法,還有最後會了解 react-router-config 的用法。

React Router Dom 的運用

React Router Dom 其實只是多了四個 React ComponentBrowserRouter、 HashRouter、Link、NavLink

關於 React Router 的運作就是建立在 historylocation 上,這邊因為是淺入淺出,就不多做探討,有興趣可以參考 React-router-dom | 原理解析 這篇文章。


基礎的路由運用

首先需要先認識幾個元件,基本上認識這幾個元件後,就能建立基本的路由切換了。

  • BrowserRouter

  • Switch

  • Link

  • Route

讓我們先初始化專案,然後進去專案在安裝需要的套件(因為筆者的範例程式碼有安裝 UI 框架,這邊可以自己取捨):

1
2
3
4
5
6
7
$ npx create-react-app react-router-test

$ cd react-router-test

$ yarn i / npm i // 擇一

$ npm i -S react-router-dom

接著將 App.js 改為下面的程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import React from 'react';
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';
import { createBrowserHistory } from "history";

// Component
import UsersComponent from './users.js';

function App() {
return (
<div className="App">
<Router>
<Switch>
<React.Fragment>
<header className="App-header">
<Link to="/home">首頁</Link>
<Link to="/users">使用者頁面</Link>

{/* 路由元件引入方式有兩種 第一種用 render */}
<Route path="/home" render={()=> (
<label>Hello World</label>
)} />

{/* 路由元件引入方式有兩種 第二種種用 component */}
<Route path="/users" component={UsersComponent} />

<Route path="/:edit" render={()=> (
<label>Hello Edit</label>
)} />
</header>
</React.Fragment>
</Switch>
</Router>
</div>
);
}

export default App;

關於 Component 怎麼引入其實就看個人,通常會建議寫在外部在整合進來。

  • BrowserRouter - 上面我們先引入 BrowserRouter 然後使用 as 簡化成 Router,因為上面原理提過,路由的核心是 History,所以這邊必須用 BrowserRouter 把全部的程式碼包起來。

  • Switch - 加不加這個其實都可以跑,但如果今天我們網址輸入 /home,其實也算是有匹配到 / 這個路由,這時候 path="/:edit" 也會一起出現,這並不是我們所預期的,所以最快的方式就是加入 Switch,這個套件會預設幫你找最近且最符合資格的路由,例如 /home

  • Link - 這個你可以把它當成是 <router-link to="/home">XXX</router-link>,最後會被轉為 <a></a> 標籤,然後可以讓使用者點擊後再轉倒頁面。

  • Route - 這個你可以把它當成是 <router-view></router-view>,如果沒用過 Vue 沒關係,透過英文我們可以知道, Route 是路線、路徑的意思,所以意思就是拿來放顯示畫面的地方。

React Router Error

這邊你可能會問那 <React.Fragment></React.Fragment> 這個 Component 是在做什麼的呢?其實筆者這邊也很納悶,沒有加之前會噴紅,加了就不會了,希望有知道的大神可以在下方留言告訴筆者,感激不盡。


巢狀的路由運用

巢狀路由其實概念大致上都差不多,只是要注意幾個小地方,我們一樣拿上面的程式碼來改(樣式一樣請自己加上哦):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import React from 'react';
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';
import { createBrowserHistory } from "history";

// Component
import UsersComponent from './users.js';

function App() {
return (
<div className="App">
<Router>
<Switch>
<React.Fragment>
<header className="App-header">
<Link to="/home">首頁</Link>
<Link to="/home/living-room">首頁</Link>
<Link to="/users">使用者頁面</Link>

{/* 路由元件引入方式有兩種 第一種用 render */}
<Route exact path="/home" render={()=> (
<label>Hello World</label>
)} />

<Route exact path="/home/living-room" render={()=> (
<label>Hello Living Room</label>
)} />

{/* 路由元件引入方式有兩種 第二種種用 component */}
<Route path="/users" component={UsersComponent} />

<Route path="/:edit" render={()=> (
<label>Hello Edit</label>
)} />
</header>
</React.Fragment>
</Switch>
</Router>
</div>
);
}

export default App;

其實大致上都差不多,但如果你的路由有第二層,像是 path="/home/living-room" 這樣,請記得在 <Route></Route> 這邊裡面加上 exact 這個屬性,如果沒有加上,你會發現透過 <Link></Link> 跳轉或是直接輸入網址導入到 /home/living-room 時,這時候 /home/ 也會被渲染出來,因為路徑匹配到了,所以通常如果不是多頁面共用的元件建議都會改成像是 <Route exact path="/home/living-room" /> 這樣。


透過 React Router Config 統一管理及渲染畫面

最後你可能會滿納悶為什麼不能透過一隻檔案去統一管理路由,像是 Vue Router 那樣,最後再透過 <router-view></router-view> 渲染呢?

1
2
3
4
5
6
7
8
9
const router = new VueRouter({
routes: [
{
path: '/user/:userId',
name: 'user',
component: User
}
]
})

剛開始可能會想說把路由整理起來,再透過 map 去渲染出來,這是一個做法,但我們可以透過套件 react-router-config 來做到這件事情,這邊只需要兩個地方,下面會提到。

首先我們先把路由整理起來:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// router.js

// Component 用 import 或是直接寫在這邊都可以
const home = () => (
<label>Hello Home</label>
);

import user from './user.js';
import password from './profile.js';
import password from './password.js';

const routes = [
{
path: '/',
title: '首頁',
exact: true,
component: home
},
{
path: '/user',
title: '使用者資料',
exact: true,
component: user,
children: [
{
path: '/user/profile',
title: '個人資訊',
exact: true,
component: profile
},
{
path: '/user/password',
title: '密碼更改',
exact: true,
component: password
}
]
}
]

然後回來 App.js,改寫下面程式碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import React from 'react';
import { BrowserRouter as Router, Switch, Link } from 'react-router-dom';
import { createBrowserHistory } from "history";

// 這次主角
import { renderRoutes } from "react-router-config";
import routes from './router.js';

// Component
import UsersComponent from './users.js';

function App() {
return (
<div className="App">
<Router>
<Switch>
<React.Fragment>
<header className="App-header">
<ul>
{
routes.map((route) => (
<li>
<Link to={route.path}>{route.title}</Link>
</li>
))
}
</ul>
{ renderRoutes(routes) }
</header>
</React.Fragment>
</Switch>
</Router>
</div>
);
}

export default App;

透過上面的方法,可以用 map 渲染出路由,不管是我們要拿來用 Link 的,或是拿來渲染畫面用的,因為我們在路由內已經都設定好了。

這邊要注意,第一個重點!!

renderRoutes() 只能渲染第一層的畫面,所以接下來我們在會經過第二層的父元件上,使用 {route} 這個參數再把子元件傳下去,馬上接著建立 user.jsprofile.jspassword.js,這三隻檔案,然後打開 user.js 這隻檔案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { renderRoutes } from "react-router-config";

const user = ({ route }) => (
<ul>
{
routes.map((route) => (
<li>
<Link to={route.path}>{route.title}</Link>
</li>
))
}
</ul>
{ renderRoutes(route.children) }
);

export default user;

這邊就是第二個重點,這邊必須帶入的是 routechildren,畫面才能渲染出來。


Conclusion & 結論

這次因為剛接觸 React 其實滿多地方還不是很熟悉,這邊先練習比較基本的 React Router,看起來這潭水還很深?!雖然剛開始沒有 v-for, v-if, v-model 很不習慣,但我想各自框架都有各自的優點,未來會努力再學到更多的知識再繼續補筆記上來。

React Router Practice

最後的最後這邊補上範例程式碼,但因為練習時有特別在拆一個首頁目錄,所以會跟上面的程式碼有些許差別,但大致上的用法都差不多。


參考網站