react-router-dom 5.X


安裝

npm install --save react-router-dom

使用

導入

import { BrowserRouter as Router, Route, Link } from "react-router-dom";
上述import是es6語法,其中as是取別名用的

簡單實例

import React from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import Home from './components/Home'
import News from './components/News'
import Music from './components/Music'

function App() {
  return (
    <Router>
      <Route exact path="/" component={Home} />{/* exact准確匹配 */}
      <Route path="/news" component={News} />
      <Route path="/music" component={Music} />
    </Router>
  );
}

export default App;

注意,每個路由必須被外層的Router包裹
exact的存在是嚴格匹配,去掉的話,訪問/news實際會渲染Home

Link的使用

類似vue的vue-router,實際每個Link默認被渲染為a標簽

<Link to='/' > 首頁</Link>
<Link to='/news'>新聞</Link>
<Link to='/music'>音樂</Link>

react-router-dom 官方示例解讀

BasicExample–基礎實例

這是一個最基礎的路由示例,根據不同的url渲染不同的組件。值得注意的是,對於Route組件而言,支持的渲染組件方式不唯一單標簽的時候可以使用component屬性,render屬性,或者children屬性掛載要渲染的組件。雙標簽的時候包裹要渲染的組件,也就是children…

import React from 'react'
import {
    BrowserRouter as Router,
    Switch,
    Route,
    Link
} from "react-router-dom";

const Home = () => <h2>Home</h2>
const About = () => <h2>About</h2>
const Dashboard = () => <h2>Dashboard</h2>
const News = () => <h2>News</h2>
const Games = () => <h2>Games</h2>

export default function () {
    return (
        <Router>
            <div>
                <ul>
                    <li> <Link to="/">Home</Link> </li>
                    <li>  <Link to="/about">About</Link>  </li>
                    <li>  <Link to="/dashboard">Dashboard</Link> </li>
                    <li>  <Link to="/news">News</Link> </li>
                    <li>  <Link to="/games">Games</Link> </li>
                </ul>
                <hr />
                <Switch>
                    <Route exact path="/"> <Home /></Route>
                    <Route path="/about" component={About} />
                    <Route path="/dashboard" children={<Dashboard />} />
                    <Route path="/news" render={()=><News />} />
                    <Route path="/games" component={()=><Games/>} />
                </Switch>
            </div>
        </Router>
    );
}

UrlParams–動態路由

 該示例演示了動態路由是如何匹配的,以及如何獲取匹配到的參數值。和很多框架匹配規則一致,都是:param.在獲取參數的時候,可以用hooks形式 ,也可以用原始的props.match.params.xxx

import React from "react";
import {
    BrowserRouter as Router,
    Switch,
    Route,
    Link,
    useParams
} from "react-router-dom";


export default function () {
    return (
        <Router>
            <div>
                <h2>Accounts</h2>
                <ul>
                    <li>
                        <Link to="/netflix/1">Netflix</Link>
                    </li>
                    <li>
                        <Link to="/zillow-group/2">Zillow Group</Link>
                    </li>
                    <li>
                        <Link to="/yahoo/3">Yahoo</Link>
                    </li>
                    <li>
                        <Link to="/modus-create/4">Modus Create</Link>
                    </li>
                </ul>
                <Switch>
                    <Route path="/:page/:num" component={Child} />
                </Switch>
            </div>
        </Router>
    );
}

function Child(props) {
    let { page} = useParams();
    let num=props.match.params.num;
    return (
        <div>
            <h3>
                當前頁: {page}--數字:{num}
            </h3>
        </div>
    );
}

Nesting–嵌套路由

 嵌套路由適用於有明顯層級划分的情況,以官方示例來看,主層級分為home和topics,topics又划分出三個子主題,這就涉及到了嵌套路由。子路由的url都是在父級路由基礎上拼接出來的。像這樣 /topics /topics/rendering。值得注意的是,這個案例里用到了一個新的hooks,useRouteMatch,這就相當於原始的props.match.此外,這個示例里對useRouteMatch()解構了path和url,如果你打印一下,你會發現它們的值是一樣的,也許就像原文解釋那樣,一個用來獲取父路徑,一個用於Link組件的跳轉,更規范?

import React from "react";
import {
    BrowserRouter as Router,
    Switch,
    Route,
    Link,
    useParams,
    useRouteMatch
} from "react-router-dom";

const Home = () => <h2>Home</h2>

const Topic = () => {
    let { topicId } = useParams();
    return <h3>{topicId}</h3>
}

const Topics = () => {

    let { path, url } = useRouteMatch();
    return (
        <div>
            <h2>Topics</h2>
            <ul>
                <li><Link to={`${url}/rendering`}>Rendering with React</Link></li>
                <li><Link to={`${url}/components`}>Components</Link></li>
                <li><Link to={`${url}/props-v-state`}>Props v. State</Link></li>
            </ul>
            <Switch>
                <Route exact path={path}> <h3>Please select a topic.</h3></Route>
                <Route path={`${path}/:topicId`} component={Topic} />
            </Switch>
        </div>
    );
}

export default function () {
    return (
        <Router>
            <div>
                <ul>
                    <li><Link to="/">Home</Link></li>
                    <li><Link to="/topics">Topics</Link></li>
                </ul>
                <hr />
                <Switch>
                    <Route exact path="/" component={Home} />
                    <Route path="/topics" component={Topics} />
                </Switch>
            </div>
        </Router>
    );
}

 

 

 

AuthRoute–路由鑒權

這個demo.核心是通過高階組件+狀態控制實現路由鑒權。在實際開發中,有些頁面必須登錄才可以訪問,甚至不同身份的人看到的頁面也是不一樣的。public頁面都可以訪問,protected頁面必須登錄才可以訪問。登錄狀態這里使用一個變量isLogin控制.Redirect 組件用於身份驗證不通過時重定向處理,useHistory 鈎子函數可獲取歷史記錄接口,控制頁面跳轉。PrivateRoute 是一個高階組件,對基礎的Route進行了進一步封裝,注意...rest,在這里相當於將高階組件獲取的path屬性傳遞給Route

import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link,
  Redirect,
  useHistory,
} from "react-router-dom";

let isLogin = false;

const LoginBtn = () =>   <button onClick={()=>{isLogin = true}}>登錄</button>

const LoginOutBtn = () => {
  let history=useHistory();
  return <button onClick={()=>{isLogin = false;history.push("/login")}}>退出登錄</button>
}

const Login = () => <LoginBtn/>
const Condition = ({ children }) => isLogin ? children : <Redirect to="/login" />

const PrivateRoute = ({ children, ...rest }) => { return ( <Route {...rest} render={() => <Condition children={children} />} /> ); } export default function () { return ( <Router> <p><Link to={"/public"} >public</Link></p> <p><Link to={"/protected"}>protected</Link></p> <Switch> <Route exact path="/" render={() => <h3>home</h3>} /> <Route path="/public" render={() => <h3>public</h3>} /> <Route path="/login" component={Login} /> <PrivateRoute path="/protected"> <h2>已經登錄 可查看-protected</h2> <LoginOutBtn/> </PrivateRoute> </Switch> </Router> ) }

CustomLink–自定義路由 

自定義路由本質是在Route組件的基礎上加入了一些定制化處理,相當於包裹了一層。為了更好理解,這里對官方示例做了個微調,強匹配屬性exact直接寫入而不是傳參形式體現。useRouteMatch可以根據path返回一個匹配結果對象,exact表示強匹配
,借助於Route組件,useRouteMatch可以空調用,像這樣useRouteMatch().反之,需要傳參。可嵌套路由,對比查看。

import React from "react";
import {
    BrowserRouter as Router,
    Switch,
    Route,
    Link,
    useRouteMatch
} from "react-router-dom";


const Home = () => <h2>Home</h2>
const About = () => <h2>About</h2>

export default function () {
    return (
        <Router>
            <div>
                <MyLink to="/" label="Home" />
                <MyLink to="/about" label="About" />
                <hr />
                <Switch>
                    <Route exact path="/" component={Home} />
                    <Route path="/about" component={About} />
                </Switch>
            </div>
        </Router>
    );
}

function MyLink({ label, to }) {
    let match = useRouteMatch({
        path: to,
        exact: true
    })
    // 這里返回的match對象僅用於樣式控制

    return (
        <div className={match ? "active" : ""}>
            {match && "> "}
            <Link to={to}>{label}</Link>
        </div>
    );
}

PreventingTransitions–阻止過渡

正常情況下,在用戶在表單中填寫了一些信息但是沒提交的情況下,點擊其他頁面跳轉鏈接,等再返回的時候,表單數據會丟失。這個例子就是提供一種阻斷方式,在進行頁面跳轉的時候給用戶一個提示,確認后會跳轉,避免因為誤操作導致的表單數據丟失。提示這里使用的是Prompt組件,when屬性為一個布爾值,true彈出提示框,message為具體的提示信息。Prompt也可以寫在form之外,保證在要渲染的組件里即可。這種場景,通常用於長表單輸入,比如注冊。關於Prompt,還有個好用的點,下邊單獨介紹。

import React, { useState } from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link,
  Prompt
} from "react-router-dom";

// Sometimes you want to prevent the user from
// navigating away from a page. The most common
// use case is when they have entered some data
// into a form but haven't submitted it yet, and
// you don't want them to lose it.

export default function PreventingTransitionsExample() {
  return (
    <Router>
      <ul>
        <li>
          <Link to="/">Form</Link>
        </li>
        <li>
          <Link to="/one">One</Link>
        </li>
        <li>
          <Link to="/two">Two</Link>
        </li>
      </ul>

      <Switch>
        <Route path="/" exact children={<BlockingForm />} />
        <Route path="/one" children={<h3>One</h3>} />
        <Route path="/two" children={<h3>Two</h3>} />
      </Switch>
    </Router>
  );
}

function BlockingForm() {
  let [isBlocking, setIsBlocking] = useState(false);

  return (
    <form
      onSubmit={event => {
        event.preventDefault();
        event.target.reset();
        setIsBlocking(false);
      }}
    >
      <Prompt
        when={isBlocking}
        message={location =>
          `Are you sure you want to go to ${location.pathname}`
        }
      />

      <p>
        Blocking?{" "}
        {isBlocking ? "Yes, click a link or the back button" : "Nope"}
      </p>

      <p>
        <input
          size="50"
          placeholder="type something to block transitions"
          onChange={event => {
            setIsBlocking(event.target.value.length > 0);
          }}
        />
      </p>

      <p>
        <button>Submit to stop blocking</button>
      </p>
    </form>
  );
}

Prompt

  • message屬性還可以接收一個函數,該函數可以獲取到下一個位置(location),返回true不提示,反之,彈出提示
<Prompt message={location =>
          location.pathname.startsWith("/one")
            ? true
            : `Are you sure you want to go to ${location.pathname}?`
        }
      />

NO Match–404

 該示例演示的是對404的處理,用於捕獲所有未匹配的項,通常放置於Switch的最后一項Route里,匹配規則為*。當然,也許你還想將所有的未捕獲頁面都跳轉到/error,這個需要使用重定向,后邊會介紹

import React from "react";
import {
  BrowserRouter as Router,
  Route,
  Link,
  Switch,
  Redirect,
  useLocation
} from "react-router-dom";



export default function NoMatchExample() {
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/old-match">Old Match, to be redirected</Link>
          </li>
          <li>
            <Link to="/will-match">Will Match</Link>
          </li>
          <li>
            <Link to="/will-not-match">Will Not Match</Link>
          </li>
          <li>
            <Link to="/also/will/not/match">Also Will Not Match</Link>
          </li>
        </ul>

        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/old-match">
            <Redirect to="/will-match" />
          </Route>
          <Route path="/will-match">
            <WillMatch />
          </Route>
        <Route path="*" >
            <NoMatch />
          </Route>
         
        </Switch>
      </div>
    </Router>
  );
}

function Home() {
  return <h3>Home</h3>;
}

function WillMatch() {
  return <h3>Matched!</h3>;
}

function NoMatch() {
  let location = useLocation();

  return (
    <div>
      <h3>
        No match for <code>{location.pathname}</code>
      </h3>
    </div>
  );
}

統一處理404

 <Switch>
  <Route path="/one" component={One}/>
  <Route path="/two" component={Two}/>
  <Route path="/error" component={Error}/>
  <Redirect from="/*" to="/error" />
</Switch>

Sidebar 側邊欄

側邊欄這個案例很常見,官方示例里邊介紹的除了基礎側邊欄,還擴展了一種多處渲染的方式。即當路由匹配到當前url時,可以在應用給程序內任何你想渲染的地方去分別渲染sideber和main,注意下邊的map遍歷,只有children屬性那里有差異

import React from "react";

import {render} from "react-dom";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";


const routes = [
  {
    path: "/",
    exact: true,
    sidebar: () => <div>home!</div>,
    main: () => <h2>Home</h2>
  },
  {
    path: "/bubblegum",
    sidebar: () => <div>bubblegum!</div>,
    main: () => <h2>Bubblegum</h2>
  },
  {
    path: "/shoelaces",
    sidebar: () => <div>shoelaces!</div>,
    main: () => <h2>Shoelaces</h2>
  }
];

export default function SidebarExample() {
  return (
    <Router>
      <div style={{ display: "flex" }}>
        <div
          style={{
            padding: "10px",
            width: "40%",
            background: "#f0f0f0"
          }}
        >
          <ul style={{ listStyleType: "none", padding: 0 }}>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/bubblegum">Bubblegum</Link>
            </li>
            <li>
              <Link to="/shoelaces">Shoelaces</Link>
            </li>
          </ul>

          <Switch>
            {routes.map((route, index) => (
             
              <Route
                key={index}
                path={route.path}
                exact={route.exact}
                
                children={<route.sidebar />}
              />
            ))}
          </Switch>
        </div>

        <div style={{ flex: 1, padding: "10px" }}>
          <Switch>
            {routes.map((route, index) => (
              <Route
                key={index}
                path={route.path}
                exact={route.exact}
                
                children={<route.main />}
              />
            ))}
          </Switch>
        </div>
      </div>
    </Router>
  );
}
render(
<SidebarExample/>,document.getElementById("root"))
  • 結合效果圖來看,側邊欄底部和右側,都進行了渲染,即多處渲染。注意,這兩次其實除了渲染的組件不同,其他都一樣

config 路由配置

 有時候,也許你希望將路由集中配置,比如放在一個數組里,每個路由對象包含path和component。涉及嵌套的,就再來一個數組,存放子路由對象。

import React from "react";
import { render } from 'react-dom'
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";


const Sandwiches = () => <h2>Sandwiches</h2>
const Bus = () => <h2>Bus</h2>
const Cart = () => <h2>Cart</h2>

const routes = [
  {
    path: "/sandwiches",
    component: Sandwiches
  },
  {
    path: "/tacos",
    component: Tacos,
    routes: [
      {
        path: "/tacos/bus",
        component: Bus
      },
      {
        path: "/tacos/cart",
        component: Cart
      }
    ]
  }
];

export default function App() {
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/tacos">Tacos</Link>
          </li>
          <li>
            <Link to="/sandwiches">Sandwiches</Link>
          </li>
        </ul>


        <Switch>
          {routes.map((route, i) => (
            <RouteWithSubRoutes key={i} {...route} />
          ))}
        </Switch>
      </div>
    </Router>
  );
}

function RouteWithSubRoutes(route) {

  return (
    <Route
      path={route.path}
      render={(props) => {
        return <route.component {...props} routes={route.routes} />
      }}
    />
  );
}

function Tacos({ routes }) {
  return (
    <div>
      <h2>Tacos</h2>
      <ul>
        <li>
          <Link to="/tacos/bus">Bus</Link>
        </li>
        <li>
          <Link to="/tacos/cart">Cart</Link>
        </li>
      </ul>

      <Switch>
        {routes.map((route, i) => (
          <RouteWithSubRoutes key={i} {...route} />
        ))}
      </Switch>
    </div>
  );
}

render(<App />, document.getElementById("root"))

Query parameters 查詢參數

 該示例其實本質是借用了瀏覽器內置的URLSearchParams,這個方法可以很方便的解析url參數,但這個存在兼容問題,放棄IE家族就沒問題了。具體URLSearchParamsAPI,可參考MDN這段示例代碼:

var paramsString = "q=URLUtils.searchParams&topic=api"
var searchParams = new URLSearchParams(paramsString);

searchParams.has("topic") === true; // true
searchParams.get("topic") === "api"; // true
searchParams.getAll("topic"); // ["api"]
searchParams.get("foo") === null; // true
searchParams.append("topic", "webdev");
searchParams.toString(); // "q=URLUtils.searchParams&topic=api&topic=webdev"
searchParams.set("topic", "More webdev");
searchParams.toString(); // "q=URLUtils.searchParams&topic=More+webdev"
searchParams.delete("topic");
searchParams.toString(); // "q=URLUtils.searchParams"
import React from "react";
import { render } from 'react-dom'
import {
  BrowserRouter as Router,
  Link,
  useLocation
} from "react-router-dom";



export default function App() {
  return (
    <Router>
      <QueryParamsDemo />
    </Router>
  );
}

//這里是重點
function useQuery() {
  return new URLSearchParams(useLocation().search);
}

function QueryParamsDemo() {
  let query = useQuery();
  return (
    <div>
      <div>
        <h2>Accounts</h2>
        <ul>
          <li>
            <Link to="/account?name=netflix">Netflix</Link>
          </li>
          <li>
            <Link to="/account?name=zillow-group">Zillow Group</Link>
          </li>
          <li>
            <Link to="/account?name=yahoo">Yahoo</Link>
          </li>
          <li>
            <Link to="/account?name=modus-create">Modus Create</Link>
          </li>
        </ul>

        <Child name={query.get("name")} />
      </div>
    </div>
  );
}

function Child({ name }) {
  return (
    <div>
      {name ? (
        <h3>
          The <code>name</code> in the query string is &quot;{name}
          &quot;
        </h3>
      ) : (
        <h3>There is no name in the query string</h3>
      )}
    </div>
  );
}
render(
<App />, document.getElementById("root"))

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM