回來重溫一下。
簡述
雖然之前有學過 v6 的版本,不過還是想來把 v5 給弄清楚一些,以免之後碰到舊專案時會踩到雷。
基本結構
在 v6 裡會用 BrowserRouter
、Routes
和 Route
來組成,v5 其實也差不多,只是多了 exact
跟 Switch
這兩個東西。
先來看 code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import { BrowserRouter, Route, Switch } from "react-router-dom" import About from "./pages/About"; import Contact from "./pages/Contact"; import Home from "./pages/Home";
export default function App() { return ( <div className="container"> <BrowserRouter> <Switch> <Route exact path="/"> <Home /> </Route> <Route path="/about"> <About /> </Route> <Route path="/contact"> <Contact /> </Route> </Switch> </BrowserRouter> </div> ); }
|
簡單說明一下:
- 元件要當作
Route
的 children,不是透過 props
Switch
的作用是「只顯示第一個匹配的 path」,否則會全部顯示出來
exact
的作用是「必須一模一樣才算」
這邊解釋一下 exact
的部分,假設沒有加的話,我到 /about
或 /contact
都會匹配到 /
的元件,為什麼?
你只要用 regex 的「部分匹配」概念來思考就行了,/about
是不是也包含 /
這個路徑?/contact
是不是也一樣?這個就是他的規則。
Nav 與 NavLink
要切換不同頁面的話一樣會透過 Link
來做,像這樣:
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
| import { BrowserRouter, Route, Switch, Link } from "react-router-dom" import About from "./pages/About"; import Contact from "./pages/Contact"; import Home from "./pages/Home";
export default function App() { return ( <div className="container"> <BrowserRouter> <nav> <h1>My articles</h1> <Link to="/">Home</Link> <Link to="/about">About</Link> <Link to="/contact">Contact</Link> </nav> <Switch> <Route exact path="/"> <Home /> </Route> <Route path="/about"> <About /> </Route> <Route path="/contact"> <Contact /> </Route> </Switch> </BrowserRouter> </div> ); }
|
這個跟 v6 沒什麼差,比較特別的是 NavLink
:
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
| import { BrowserRouter, Route, Switch, NavLink } from "react-router-dom" import About from "./pages/About"; import Contact from "./pages/Contact"; import Home from "./pages/Home";
export default function App() { return ( <div className="container"> <BrowserRouter> <nav> <h1>My articles</h1> <NavLink exact to="/">Home</NavLink> <NavLink to="/about">About</NavLink> <NavLink to="/contact">Contact</NavLink> </nav> <Switch> <Route exact path="/"> <Home /> </Route> <Route path="/about"> <About /> </Route> <Route path="/contact"> <Contact /> </Route> </Switch> </BrowserRouter> </div> ); }
|
這個的用意是,當我在 /
時它會自動幫我在對應的 NavLink
加上 .active
的 class,所以你可以利用這個來處理「目前所在位置」的樣式,就不用再自己寫 expressin 來判斷了。
至於 exact
的用途就跟前面說的一樣,是用來避免「部分匹配」的問題(在 /about
頁面 /
也會被加上 .active
)。
useHistory
有些時候可能需要手動切換到某路由,在 v6 的時候會用 useNavigate
,v5 只是換成 useHistory
而已:
1 2 3 4 5 6 7
| export default function Article() { const history = useHistory() useEffect(() => { if (!error) return setTimeout(() => history.push('/'), 2000) }, [error, history]) }
|
關於 404 頁面
當使用者到了「不存在的路由」時,可能會希望一律導向至特定的頁面(例如 404),這時候就可以用下面的方式來做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import { Redirect } from "react-router-dom" <Switch> <Route exact path="/"> <Home /> </Route> <Route path="/about"> <About /> </Route> <Route path="/contact"> <Contact /> </Route> <Route path="/article/:id"> <Article /> </Route> <Route path="*"> <Redirect to="/" /> </Route> </Switch>
|
當 React 匹配不到前面的任何一個時,就會套用 *
的路徑,接著再透過 Redirect
導向到我們想要的地方(可以是 404 頁面)。
另外你可能會想說,那如果這樣寫呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <Switch> <Route exact path="/"> <Home /> </Route> <Route path="/about"> <About /> </Route> <Route path="/contact"> <Contact /> </Route> <Route path="/article/:id"> <Article /> </Route> <Route path="*"> // 直接顯示 Home 元件 <Home /> </Route> </Switch>
|
如果單看畫面的話這樣寫沒問題,可是有一個要注意的地方是「網址不會變」,什麼意思?
意思是假設我到 /asoqekjwiq
時,雖然會如期顯示 Home
的內容,但網址依舊會停留在 /asoqekjwiq
這個看起來很怪的路徑。可是如果是透過 Redirect
的話,網址就會因為跳轉的關係被更新為 /
。
因此從這個結果來看我會覺得 Redirect
是比較合理的選擇。
queryString
當我們想取得網址上的 ?name=peanu&age=24
這種 queryString 時,不是透過 path
來建立對應路由,而是透過 useLocation
跟 URLSearchParams
來實作。
直接來看例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import React from 'react' import { useLocation } from "react-router-dom"
export default function Contact() { const queryString = useLocation().search const queryParams = new URLSearchParams(queryString) const name = queryParams.get('name')
return ( <div> <h2>Hey {name}, Contact us...</h2> <p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Eius harum rerum repellat incidunt officiis molestias vitae tempora deserunt deleniti labore optio nobis ipsam odio aliquam, soluta dolorum praesentium aut? Fugit.</p> </div> ) }
|
附註:useLocation
是 react-router-dom 提供的 hook,URLSearchParams
是元生 JS 的東西,別搞混了。
其實就這樣子,沒什麼複雜的,但會用到它的時機也許還蠻多的。
路由守衛
其實就是一種運用 Redirect
的小技巧,因為這個方式只適用於 V5,所以簡單補充一下。
一般網頁都會有「權限管理機制」,就是說假設我沒登入的話就不能造訪某某頁的這種規則。那這個時候該怎麼寫比較好?參考下面的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const { user } = useAuthContext()
<BrowserRouter> <Nav /> <Switch> <Route exact path="/"> {user ? <DashBoard /> : <Redirect to="/login" />} </Route> <Route path="/login"> {!user ? <Login /> : <Redirect to="/" />} </Route> <Route path="/signup"> {!user ? <Signup /> : <Redirect to="/" />} </Route> <Route path="/create"> {user ? <Create /> : <Redirect to="/login" />} </Route> <Route path="/project/:id"> {user ? <Project /> : <Redirect to="/login" />} </Route> </Switch> </BrowserRouter>
|
我只要運用運算來決定要真正要顯示的 Route
,就可以做到權限管理的限制。像是 user
如果不存在的話就不能造訪 <DashBoard>
,而是會被重新導向到 /login
去。