1. 程式人生 > 其它 >React Router(react-router-dom V6 整理)

React Router(react-router-dom V6 整理)

官方文件

一個神奇的連結: React Router 官方文件

安裝

執行以下命令安裝React Router:

npm install react-router-dom@6 --save

注意:react-router-dom 包含所有內容,匯入元件時應該從react-router-dom中匯入,而不應該從 react-router中匯入,否則,會意外地在應用中匯入不匹配的庫版本;

基本用法

在Web應用程式中開啟 React Router 功能

// index.js

import React from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';

const container = document.getElementById('root');
const root = createRoot(container);
root.render(
  // 通過在應用入口新增 BrowserRouter 元件開啟 React Router 功能
  <BrowserRouter>
    <React.StrictMode>
      <App />
    </React.StrictMode>
  </BrowserRouter>
);

注意:web 應用程式中一般使用 BrowserRouter 元件, 還用另一種 HashRouter 元件方式;
這兩種方式的區別:

  1. 底層原理不一樣:
    BrowserRouter呼叫的是H5 history API,低版本相容性問題。
    HashRouter 使用的是URL雜湊值
  2. 位址列表現形式不一樣:
    BrowserRouter的路徑:localhost:3000/index
    HashRouter的路徑:localhost:3000/#/index
  3. 重新整理後對路由state引數的影響
    BrowserRouter沒有任何影響,因為state儲存在history物件中
    HashRouter重新整理後會導致路由state引數的丟失

值得注意的是,官方強烈建議不要使用 HashRouter;

配置路由

點選檢視程式碼
// App.js

// 匯入 Route, Routes 元件
import { Route, Routes } from 'react-router-dom';

function App() {
  return (
    <Routes>
	  {/* 頁面預設導航到 Home 元件(頁面上顯示 Home Compontent) */}
      <Route path='/' element={<Home />} />
	  {/* 在地址輸入 http://localhost:3000/about 導航到 About 元件(頁面上顯示 About Compontent) */}
      <Route path='/about' element={<About />} />
    </Routes>
  );
}

const Home = (props) => {
  return <div>Home Compontent</div>;
}

const About = (props) => {
  return <div>About Compontent</div>;
}

export default App;

在以前版本的 React Router 中,必須以某種方式對路由進行排序,以便在多個路由與不明確的 URL 匹配時獲得要呈現的正確路由。V6更智慧,將選擇最具體的匹配;

新增 "不匹配" 路由

點選檢視程式碼
// App.js

import { Route, Routes } from 'react-router-dom';

function App() {
  return (
    <Routes>
      <Route path='/' element={<Home />} />
      <Route path='/about' element={<About />} />
      {/* 當沒有其他路由與 URL 匹配時,匹配 path='*'的路由 */}
      <Route path='*' element={<NotFount />} />
    </Routes>
  );
}

const Home = (props) => {
  return <div>Home Compontent</div>;
}

const About = (props) => {
  return <div>About Compontent</div>;
}

const NotFount = (props) => {
  return <div>NotFount !!!</div>;
}

export default App;

當沒有其他路由與 URL 匹配時,才會匹配 path='*'路由。此路由將匹配任何 URL,但優先順序最弱,因此路由器僅在沒有其他路由匹配時才會選擇它;

使用連結導航

點選檢視程式碼
// App.js

// 匯入 Link 元件
import { Route, Routes, Link } from 'react-router-dom';

function App() {
  return (
    <Routes>
	  {/* 頁面預設導航到 Home 元件(渲染 Home 元件, 頁面顯示 About Compontent 連結) */}
      <Route path='/' element={<Home />} />
      <Route path='/about' element={<About />} />
    </Routes>
  );
}

const Home = (props) => {
  return <div>
    {/* 點選 About Link 連結跳轉至 http://localhost:3000/about
        畫面顯示 About 元件內容(Home Link連結)
      */}
    <Link to='/about'>About Link</Link>
  </div>;
}

const About = (props) => {
  return <div>
    {/* 點選 Home Link 連結跳轉至 http://localhost:3000/
        畫面顯示 Home 元件內容(About Link連結)
      */}
    <Link to='/'>Home Link</Link>
  </div>;
}

export default App;

使用巢狀路由

點選檢視程式碼
// App.js

// 匯入 Link, Outlet 元件
import { Route, Routes, Link, Outlet } from 'react-router-dom';

function App() {
  return (
    <Routes>
      <Route path='/' element={<Home />} >
        <Route path='about' element={<About />} />
        <Route path='setting' element={<Setting />} />
        {/* 預設子路由
            如果導航欄地址為 http://localhost:3000,此時子路由渲染位置(Outlet)為空白,
            增加以下配置,子路由渲染位置(Outlet)渲染 <List />
         */}
        <Route index element={<List />} />
      </Route>
    </Routes>
  );
}

const Home = (props) => {
  return <>
    <div>
      <Link to='/about'>About Link</Link> | {" "}
      <Link to='/setting'>Setting Link</Link>
    </div>
    <div style={{padding: '20px', margin: '10px', borderTop: '1px solid'}}>
      {/* Outlet 為巢狀子路由的出口,比如:點選 About Link 連結,
          瀏覽器地址變為 http://localhost:3000/about
          在此渲染路由地址為 /about 的元件(在此顯示: About Compontent)
       */}
      <Outlet />
    </div>
  </>;
}

const About = (props) => {
  return <div>
    About Compontent
  </div>;
}

const Setting = (props) => {
  return <div>
    Setting Compontent
  </div>;
}

const List = (props) => {
  return <div>
    List Compontent
  </div>;
}

export default App;

這是 React Router 最強大的功能之一,在實際開發中,大多數 UI 都是一系列巢狀佈局,React Router 通過這種巢狀路由的方式實現了一些自動、持久的佈局處理;

使用活動連結

點選檢視程式碼
// App.js

// 匯入 NavLink 元件
import { Route, Routes, NavLink, Outlet } from 'react-router-dom';

function App() {
  return (
    <Routes>
      <Route path='/' element={<Home />} >
        <Route path='about' element={<About />} />
        <Route path='setting' element={<Setting />} />
        <Route index element={<List />} />
      </Route>
    </Routes>
  );
}

const Home = (props) => {
  return <>
    <div>
      {/* <NavLink /> 接收一個style 或者 className 屬性
          屬性值為一個回撥函式,可以通過 isActive 的值判斷
          連結是否處於活動狀態,從而實現給活動連結節點新增樣式的效果
          示例效果:點選哪個連結,目標連結字型變紅
       */}
      <NavLink
        style={({ isActive }) => navColor(isActive)}
        to='/about'
      >
        About Link
      </NavLink> | {" "}
      <NavLink
        style={({isActive}) => navColor(isActive)}
        to='/setting'
      >
        Setting Link
      </NavLink>
    </div>
    <div style={{ padding: '20px', margin: '10px', borderTop: '1px solid' }}>
      <Outlet />
    </div>
  </>;
}

const About = (props) => {
  return <div>
    About Compontent
  </div>;
}

const Setting = (props) => {
  return <div>
    Setting Compontent
  </div>;
}

const List = (props) => {
  return <div>
    List Compontent
  </div>;
}

const navColor = (isActive) => {
  return {color: isActive ? 'red' : ""}
}

export default App;
主要實現了給當前啟用的連結設定一個式樣,支援 **style** 和 **className** 這兩種屬性;

讀取 URL 引數

點選檢視程式碼
// 匯入 useParams 元件
import { Route, Routes, NavLink, Outlet, useParams } from 'react-router-dom';

function App() {
  return (
    <Routes>
      <Route path='/' element={<Home />} >
        <Route path='list' element={<List />} >
          <Route path=':id' element={<Item />} />
        </Route>
      </Route>
    </Routes>
  );
}

const Home = (props) => {
  return <>
    <div>
      <NavLink
        style={({ isActive }) => navColor(isActive)}
        to='/list'
      >
        List Link
      </NavLink>
    </div>
    <div style={{ padding: '20px', margin: '10px', borderTop: '1px solid' }}>
      <Outlet />
    </div>
  </>;
}

const Item = (props) => {
  // 從 URL 獲取引數::id
  const params = useParams();
  return <h2>Item: {params.id}</h2>;
}

const List = (props) => {
  const list = [
    {
      name: "趙雲",
      no: 100
    },
    {
      name: "馬超",
      no: 101
    }
  ]
  return <div>
    {list.map((item) => {
      return (<NavLink
        style={({isActive}) => navColor(isActive)}
        to={`/list/${item.no}`}
        key={item.no}
      >
        {item.name}
      </NavLink>)
    })}
    <div className='content'>
      {/* 指定子路由 /list/? 的渲染位置 */}
      <Outlet />
    </div>
  </div>;
}

const navColor = (isActive) => {
  return {
    color: isActive ? 'red' : "",
    marginRight: '10px'
  }
}

export default App;

與 V5 的區別

  1. <Routes> 替代 <Switch>
    寫法上的比較:
    // v5 寫法
    // 引入 react-router
    import { Route, Switch } from 'react-router-dom';
    function App() {
      return (
    	  <Switch>
    		{/* 路由配置 */}
    	  </Switch>
      );
    }
    
    // v6 寫法
    import { Route, Routes } from 'react-router-dom';
    function App() {
      return (
    	  // Routes 替換 Switch
    	<Routes> 
    	  {/* 路由配置 */}
    	</Routes>
      );
    }
    
  2. <Route> 不再支援子元件,改為使用 element 屬性;並且不再需要 exact 屬性了
    寫法上的比較:
    // v5 寫法
    // 引入 react-router
    import { Route, Switch } from 'react-router-dom';
    function App() {
      return (
    	  <Switch>
    		<Route exact path='/home'>
    		  <Home />
    		</Route>
    	  </Switch>
      );
    }
    
    // v6 寫法
    import { Route, Routes } from 'react-router-dom';
    function App() {
      return (
    	<Routes> 
    	  <Route path='/home' element={<Home /> } />
    	</Routes>
      );
    }
    
  3. 移除了 <NavLink>activeClassName 屬性
    v6寫法:
    import { NavLink } from 'react-router-dom';
    function App() {
      return (
    	<>
    	  {/* className 寫法 */}
    	  <NavLink 
    		className={({isActive}) => {
    		  return isActive ? "highlight" : "";
    		}} 
    		to="home">Home</NavLink>
    	  {/* style 寫法 */}
    	  <NavLink 
    		to="about"
    		style={({isActive}) => {
    		  return {
    			color: isActive ? "red" : ""
    		  }
    		}}
    	  >About</NavLink>
    	</>
      );
    }
    
  4. 移除 <Redirect>,改為使用 <Navigate>
    寫法上的對比:
    // v6 寫法
    import { Navigate, Route, Routes } from 'react-router-dom';
    function App() {
      return (
    	<Routes>
    	  <Route path='/' element={<Navigate replace to="/home" />} /> 
    	</Routes>
      );
    }
    
  5. <Link to>支援相對位置
    // If your routes look like this
    <Route path="app">
    <Route path="dashboard">
     <Route path="stats" />
    </Route>
    </Route>
    
    // and the current URL is /app/dashboard (with or without
    // a trailing slash)
    <Link to="stats">               => <a href="/app/dashboard/stats">
    <Link to="../stats">            => <a href="/app/stats">
    <Link to="../../stats">         => <a href="/stats">
    <Link to="../../../stats">      => <a href="/stats">
    
  6. 新增 <Outlet>
    關於 <Outlet>,參考本文的巢狀路由節點; 此元件是一個佔位符,告訴 React Router 巢狀的內容應該放到哪裡; 通過 <Outlet> 可以將所有的路由(巢狀的子路由)配置合併在一起,可進行路由的統一管理,增加了程式碼可維護性;
  7. 使用 useNavigate 實現程式設計式導航,從而代替 useHistory
    // v6 寫法
    import { useNavigate } from 'react-router-dom';
    function App() {
      const navigate = useNavigate();
    
      const handleClick = () => {
    	navigate('/home');  // push
    	// 重定向
    	// navigate('/home', {replace: true});
      };
    
      return (
    	<div>
    	  <button onClick={handleClick}>返回首頁</button>
    	</div>
      );
    }
    
  8. 一系列的 Hooks
    hooks名 作用 說明
    useParams 返回當前引數 根據路徑讀取引數
    useNavigate 返回當前路由 代替原有V5中的 useHistory
    useOutlet 返回根據路由生成的element
    useLocation 返回當前的location 物件
    useRoutes 同Routers元件一樣,只不過是在js中使用
    useSearchParams 用來匹配URL中?後面的搜尋引數

總結

本文主要記錄了一下 React Router V6 的一些基本用法以及對V5的比較,有了這些知識的支撐,足以應付大多數日常開發了;v6 版本基於全新的路由演算法帶來強大的功能和 hooks,並且重新實現了 useNavigate 來替代 useHistory ,整體上更加好理解;
先記錄這麼多,後續持續更新!!!