React router: 构建多页的 SPA (V5)

以下是 V5 的语法,V6 的语法与之区别很大。

使用 Router 可以构建多页的 Single-Page App

1 安装 react router :

npm intall react-router-dom

2 基本用法

2.1 App.js 导入Route 组件

import { Route } from "react-router-dom";

2.2 App.js 定义路径:

共两条路径:

http://localhost:3000/welcome
http://localhost:3000/products

不同的路径加载相对应的组件。

// import Route from the library
import { Route } from "react-router-dom";
import Welcome from "./components/Welcome";
import Products from "./components/Products";

function App() {
  return (
    <div>
      <Route path="/welcome">
        <Welcome />
      </Route>
      <Route path="/products">
        <Products />
      </Route>
    </div>
  );
}

export default App;

2.3 index.js 代码:

import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";

import "./index.css";
import App from "./App";

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

由于Welcome.js 和 Products.js 是与route 相关的组件,比起src/components,把它们放在名称为src/pages的文件夹中更恰当。

3 添加页面链接

3.1 标准链接方式,使用 <a> 实现页面导航。

此方法有一个缺点:每次点击链接,页面都会重新加载,App 重启,因此会丢失当前状态。

// MainHeader.js
const MainHeader = () => {
  return (
    <header>
      <nav>
        <ul>
          <li>
            <a href="/welcome">Welcome</a>
          </li>
          <li>
            <a href="/products">Products</a>
          </li>
        </ul>
      </nav>
    </header>
  );
};
export default MainHeader;
// App.js 
function App() {
  return (
    <div>
      <MainHeader />
      <main>
        <Route path="/welcome">
          <Welcome />
        </Route>
        <Route path="/products">
          <Products />
        </Route>
      </main>
    </div>
  );
}

3.2 使用 react-router-domLink 组件

<a> 改成 <Link> ,然后href 改成 to,如下的代码将阻止浏览器默认行为,页面不会重新加载:

import { Link } from "react-router-dom";
const MainHeader = () => {
  return (
    <header>
      <nav>
        <ul>
          <li>
            <Link to="/welcome">Welcome</Link>
          </li>
          <li>
            <Link to="/products">Products</Link>
          </li>
        </ul>
      </nav>
    </header>
  );
};

export default MainHeader;

3.3 NavLinkactiveClassName

使用 Link 组件虽然能避免页面重新加载,但是,用户无法直接看出当前点击了哪一个链接。
Link 改成 NavLink 就能使用 activeClassName 对当前链接添加 CSS 样式,对链接进行样式设定,

import { NavLink } from "react-router-dom";
import classes from "./MainHeader.module.css";
const MainHeader = () => {
  return (
    <header className={classes.header}>
      <nav>
        <ul>
          <li>
            <NavLink activeClassName={classes.active} to="/welcome">
              Welcome
            </NavLink>
          </li>
          <li>
            <NavLink activeClassName={classes.active} to="/products">
              Products
            </NavLink>
          </li>
        </ul>
      </nav>
    </header>
  );
};
export default MainHeader;
// MainHeader.module.css
.header {
  width: 100%;
  height: 5rem;
  background-color: #044599;
  padding: 0 10%;
}

.header nav {
  height: 100%;
}

.header ul {
  height: 100%;
  list-style: none;
  display: flex;
  padding: 0;
  margin: 0;
  align-items: center;
  justify-content: center;
}

.header li {
  margin: 0 1rem;
  width: 5rem;
}

.header a {
  color: white;
  text-decoration: none;
}

.header a:hover,
.header a:active,
.header a.active {
  color: #95bcf0;
  padding-bottom: 0.25rem;
  border-bottom: 4px solid #95bcf0;
}

4 动态路由

4.1 注册带参数的动态路由

例如,要添加一系列 products 的详情页:

冒号必须加,

   <Route path="/product-detail/:productId">
      <ProductDetail />
    </Route>

:productId 是占位符,是 dynamic segment
此语法告诉 React Router, 为路径:

ourdomain.com/product-detail/ <any thing...>

加载 <ProductDetail /> 页面

4.2 使用 useParams 提取动态参数

钩子函数 useParams 返回一个key value对组成的对象,key 是动态路径的参数名。

注册路径:<Route path="/product-detail/:productId">
输入路径 : localhost:3000/product-detail/product1
params.productId 的值为 product1

import { useParams } from "react-router-dom";

const ProductDetail = () => {
  const params = useParams();
  return (
    <section>
      <h1>Product Detail</h1>
      <p>{params.productId}</p>
    </section>
  );
};
export default ProductDetail;

5 使用 switchexact 配置路由

当前的问题,默认加载 全部 匹配输入路径的路由:

function App() {
  return (
    <div>
      <MainHeader />
      <main>
        <Route path="/welcome">
          <Welcome />
        </Route>
        <Route path="/products">
          <Products />
        </Route>
        <Route path="/products/:productId">
          <ProductDetail />
        </Route>
      </main>
    </div>
  );
}

例如:

http://localhost:3000/products/p2

此路径匹配两个路由: /products/products/:productId 因此同时加载 两个 页面。
/products/:productId/products 开头,所以匹配两个路径,与路由注册前后顺序无关。同时加载多个页面,有时,这是期望的行为,有时则不是,没有对错之分。

5.1 使用 Switch 组件实现唯一匹配

Switch 组件保证一个路径只加载一个路由,并且是 第1个 相匹配的路由:
按照以下的 Route 注册顺序, /products/:productId 不会被加载,因为是第2个匹配到的路由,只有第1个匹配的路由:/products 被加载。
(至少 React Router 5 的行为是这样)

import { Route, Switch } from "react-router-dom";
import Welcome from "./pages/Welcome";
import Products from "./pages/Products";
import MainHeader from "./components/MainHeader";
import ProductDetail from "./pages/ProductDetail";

function App() {
  return (
    <div>
      <MainHeader />
      <main>
        <Switch>
          <Route path="/welcome">
            <Welcome />
          </Route>
          <Route path="/products">
            <Products />
          </Route>
          <Route path="/products/:productId">
            <ProductDetail />
          </Route>
        </Switch>
      </main>
    </div>
  );
}
export default App;

5.2 exact 属性

为了解决上述 /products/:productId 不会被加载的问题,有两个 solution:

5.2.1 调整路由顺序

例如,调用两个匹配的路由的上下顺序:
因此 /products/p1 将匹配/products/:productId而不是 /products

<Route path="/products/:productId">
	<ProductDetail />
</Route>
<Route path="/products">
	<Products />
</Route>

5.2.2 使用 exact 属性实现精确匹配

保持原始路由顺序不变,特定的路由添加 exact 属性,告诉 react router,当且仅当路径 完全匹配 时匹配路由。

下面的 code,/products/xyz 不再匹配 /products/:productId

function App() {
  return (
    <div>
      <MainHeader />
      <main>
        <Switch>
          <Route path="/welcome">
            <Welcome />
          </Route>
          <Route path="/products" exact>
            <Products />
          </Route>
          <Route path="/products/:productId">
            <ProductDetail />
          </Route>
        </Switch>
      </main>
    </div>
  );
}

6 嵌套路由

App.js 并非唯一可以设置路由的组件。路由也可以设置路由,所有的组件都可以添加路由:例如:

// Welcome.js
import { Route } from "react-router-dom";
const Welcome = () => {
  return (
    <section>
      <h1>The welcome page</h1>
      <Route path="/welcome/new-user">
        <p>Welcome, new user!</p>
      </Route>
    </section>
  );
};
export default Welcome;

7 使用 Redirect 组件实现重定向

下面的例子,如果用户输入/ 自动重定向到路由 /welcome, <Route path="/" exact>exact 属性很重要,因为 / 匹配任意路由,没有此属性会无限循环。

import { Route, Switch, Redirect } from "react-router-dom";
import Welcome from "./pages/Welcome";
import Products from "./pages/Products";
import MainHeader from "./components/MainHeader";
import ProductDetail from "./pages/ProductDetail";

function App() {
  return (
    <div>
      <MainHeader />
      <main>
        <Switch>
          <Route path="/" exact>
            <Redirect to="/welcome" />
          </Route>
          <Route path="/welcome">
            <Welcome />
          </Route>
          <Route path="/products" exact>
            <Products />
          </Route>
          <Route path="/products/:productId">
            <ProductDetail />
          </Route>
        </Switch>
      </main>
    </div>
  );
}
export default App;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值