React Router(下):略复杂路由场景的应用

这是我参与更文挑战的第9天,活动详情查看: 更文挑战

通过 URL 传递参数

通过 URL 传递参数是很常见的需求,比如显示文章/商品的 ID。 推荐将参数作为路径的一部分(而不是查询参数),这样做对搜索引擎更友好、更有语义。

一、如何通过 URL 传递参数

演示一个传递 articleId 的例子 ?

function App() {
  return (
    <Router>
      <nav>
        <ul>
          <li>
            <Link to="/article/1">aritcle 1</Link>
          </li>
          <li>
            <Link to="/article/2">aritcle 2</Link>
          </li>
          <li>
            <Link to="/article/3">aritcle 3</Link>
          </li>
        </ul>
      </nav>
      <div class="page-container">
        <Route path="/article/:id" component={Article}></Route>
      </div>
    </Router>
  );
}
复制代码

二、如何获取参数

对于 React Router render 的组件通过高阶组件的方式,会传递一个 match 的属性,在 match.props 中可以访问到传递的参数。

  • Hook – useParams,React Router v5.1 新增
import { useParams } from "react-router-dom";

// 注意:这时的组件是被包裹在 <Route> 里
<Route path="/blog/:slug">
  <BlogPost />
</Route>;

function BlogPost() {
  let { slug } = useParams();
  return <div>Now showing post {slug}</div>;
}
复制代码
  • 函数组件通过 ({ match }) => () 获得参数
function Article({ match }) {
  return <div> {match.params.id} </div>;
}
复制代码
  • 类组件通过 this.props.match
class Article extends React.Component {
  render() {
    const { match } = this.props;
    return <div>{match.params.id}</div>;
  }
}
复制代码

如果要使用 match 的话,组件必须写在 component 中(<Route component={Component} />)。

三、路径的模糊匹配

path-to-regexp 是一个将字符串路径转换为正则表达式的的工具库,React Router 就用它来处理 url 中地址与参数。

const { pathToRegexp } = require("path-to-regexp");
复制代码

?: 匹配 0 次或 1 次

const regexp = pathToRegexp("/:foo/:bar?");
// keys = [{ name: 'foo', ... }, { name: 'bar', prefix: '/', modifier: '?' }]

regexp.exec("/test");
//=> [ '/test', 'test', undefined, index: 0, input: '/test', groups: undefined ]

regexp.exec("/test/route");
//=> [ '/test/route', 'test', 'route', index: 0, input: '/test/route', groups: undefined ]
复制代码

*:匹配 0 次或多次

const regexp = pathToRegexp("/:foo*");
// keys = [{ name: 'foo', prefix: '/', modifier: '*' }]

regexp.exec("/");
//=> [ '/', undefined, index: 0, input: '/', groups: undefined ]

regexp.exec("/bar/baz");
//=> [ '/bar/baz', 'bar/baz', index: 0, input: '/bar/baz', groups: undefined ]
复制代码

+:匹配一次或多次

const regexp = pathToRegexp("/:foo+");
// keys = [{ name: 'foo', prefix: '/', modifier: '+' }]

regexp.exec("/");
//=> null

regexp.exec("/bar/baz");
//=> [ '/bar/baz','bar/baz', index: 0, input: '/bar/baz', groups: undefined ]
复制代码

自定义匹配

通过括号包裹一个自定义的正则。

const regexpNumbers = pathToRegexp("/icon-:foo(\\d+).png");
// keys = [{ name: 'foo', ... }]

regexpNumbers.exec("/icon-123.png");
//=> ['/icon-123.png', '123']

regexpNumbers.exec("/icon-abc.png");
//=> null

const regexpWord = pathToRegexp("/(user|u)");
// keys = [{ name: 0, ... }]

regexpWord.exec("/u");
//=> ['/u', 'u']

regexpWord.exec("/users");
//=> null
复制代码

注意: 这里的匹配数字 \\d+ 和正则表达式 \d+ 不同,React Router 中选择 \d+

四、何时使用 URL 传递参数

页面状态尽量使用 URL 参数定义,扩展性更好。

比如一个考勤系统里的考勤信息页面,查询的月份信息是否要放入 URL 中?考虑到可能需要访问「指定月份」的考勤信息,所以将月份信息放任 URL 参数中更合适。

router-app.png

嵌套路由

嵌套路由是前端特有的概念:

  • 每个 React 组件都可以是一个容器组件
  • React Router 的声明式语法可以方便的定义嵌套路由

未命名文件 (4).jpg

第一层路由是常规写法

export default function App() {
  return (
    <Router>
      <div>
        <ul>
          <li>
            <NavLink exact to="/">
              Home
            </NavLink>
          </li>
          <li>
            <NavLink to="/about">About</NavLink>
          </li>
          <li>
            <NavLink to="/topics">Topics</NavLink>
          </li>
        </ul>

        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/topics">
            <Topics />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

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

function About() {
  return <h2>About</h2>;
}
复制代码

第二层路哟还是常规写法

Topic 组件:

function Topics() {
  return (
    <div>
      <h2>Topics</h2>

      <ul>
        <li>
          <NavLink to="/topics/components">Components</NavLink>
        </li>
        <li>
          <NavLink to="/topics/props-v-state">Props v. State</NavLink>
        </li>
      </ul>
      <Switch>
        <Route path="/topics/:topicId">
          <Topic />
        </Route>
        <Route path="/topics">
          <h3>Please select a topic.</h3>
        </Route>
      </Switch>
    </div>
  );
}
复制代码

可以看到嵌套的第二层路由也是常规操作,这里利用了 <Route> 的共存属性,多次匹配同一个路径渲染不同的组件。

第一层路由匹配部分也可以用 useRouteMatch 来完成:

useRouteMatch:尝试以与相同的方式匹配当前 URL。 React-Router v5.1 新带来的 Hook。

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

function Topics() {
  let match = useRouteMatch();
  /*
    {
      isExact: false,
      params: {},
      path: "/topics",
      url: "/topics",
    };
  */
  return (
    <div>
      <h2>Topics</h2>

      <ul>
        <li>
          <NavLink to={`${match.url}/components`}>Components</NavLink>
        </li>
        <li>
          <NavLink to={`${match.url}/props-v-state`}>Props v. State</NavLink>
        </li>
      </ul>

      <Switch>
        <Route path={`${match.path}/:topicId`}>
          <Topic />
        </Route>
        <Route path={match.path}>
          <h3>Please select a topic.</h3>
        </Route>
      </Switch>
    </div>
  );
}
复制代码

Topic 组件

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

function Topic() {
  const { topicId } = useParams();
  return <h3>Requested topic ID: {topicId}</h3>;
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享