React 系列十二:React - Router

快来加入咱们吧!

"小和山的菜鸟们",为前端开发者提供技术相关资讯以及系列基础文章。为更好的用户体验,请您移至咱们官网小和山的菜鸟们 ( xhs-rookies.com/ ) 进行学习,及时获取最新文章。javascript

"Code tailor" ,若是您对咱们文章感兴趣、或是想提一些建议,微信关注 “小和山的菜鸟们” 公众号,与咱们取的联系,您也能够在微信上观看咱们的文章。每个建议或是赞同都是对咱们极大的鼓励!css

前言

这节咱们将介绍 Reactreact - router,路由跳转的配置以及使用前端

本文会向你介绍如下内容:java

  • 认识 react-router
  • react-router 基本使用
  • react-router 高级使用
  • react-router-config

认识 react - router

若是你是第一次接触 router 这个名词,能够先去这里补充一下本身的路由知识再来往下阅读哦node

**注意:**如下内容基于 react-router v5 版本,若是和读者当前使用的不符合,请以官网为主react

安装 react-routerweb

yarn add react-router-dom

npm install react-router-dom
复制代码

什么是 react-routernpm

React Router is a set of navigation components that are combined with your application in a declarative manner. Whether you want to provide bookmarkable URLs for your web applications or use composable navigation methods in React Native, React Router can be used anywhere React renders数组

react-router 是一组以声明方式与您的应用程序组合的导航组件,换句话,react-routerReact 体系中的路由库,它经过管理 URL,实现组件的切换和状态的变化浏览器

react-router 基本使用

React Router 中的组件主要分为三类:

路由器 BrowserRouterHashRouter

  • BrowserRouter 使用常规URL路径,建立一个像 example.com/some/path 这样真实的 URL ,可是他们要求正确的配置服务器
  • HashRouter 将当前位置存储在 URL 的哈希部分中,所以 URL 看起来相似于http://example.com/#/your/page ,因为哈希从不发送到服务器,所以这意味着不须要特殊的服务器配置

二者之间的主要区别是它们存储 URL 和与 Web 服务器通讯的方式

路由匹配器,例如 Switch 和 Route:

当渲染 Switch 组件时,它将搜索它的 Route 子元素,以查找路径与当前 URL 匹配的元素,它将呈现找到的第一个 Route 并忽略全部的其余路由。这意味着您应该将包含更多特定路径的 Route 放在不那么特定的路由以前

Switch

咱们来看下面的路由规则:

  • 当咱们匹配到某一个路径时,咱们会发现有一些问题;
  • 好比/about 路径匹配到的同时,/:userid也被匹配到了,而且最后的一个 NoMatch 组件老是被匹配到;
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/profile" component={Profile} />
<Route path="/:userid" component={User}/>
<Route component={NoMatch}/>
复制代码

缘由是什么呢?默认状况下,react-router 中只要是路径被匹配到的 Route 对应的组件都会被渲染;

可是实际开发中,咱们每每但愿有一种排他的思想:

  • 只要匹配到了第一个,那么后面的就不该该继续匹配了;
  • 这个时候咱们可使用 Switch 来将全部的 Route 进行包裹便可;
<Switch>
  <Route exact path="/" component={Home} />
  <Route path="/about" component={About} />
  <Route path="/profile" component={Profile} />
  <Route path="/:userid" component={User} />
  <Route component={NoMatch} />
</Switch>
复制代码

Route

它最基本的职责是在其路径与当前 URL 匹配时呈现一些 UI

Route 的渲染方法一共有三类

Route component

仅在位置匹配时根据 route props 渲染

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

// All route props (match, location and history) are available to User
function User(props) {
  return <h1>Hello {props.match.params.username}!</h1>;
}

ReactDOM.render(
  <Router> <Route path="/user/:username" component={User} /> </Router>,
  node
);
复制代码

当你在使用此方法加载时,router 一般会根据给定的 component 去调用 React.createElement 建立一个新的 React 元素。这也就意味着若是你在此是用内联函数渲染的组件,那么它将会在每次渲染时都建立一个新的函数,即卸载组件安装新有组件,而不是更新。因此当你使用内联函数渲染组件时,请选择 renderchildren 方法

Route render

你能够传入一个在位置匹配时调用的函数,而不是使用组件 prop 为您建立一个新的 React 元素

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

// convenient inline rendering
ReactDOM.render(
  <Router> <Route path="/home" render={() => <div>Home</div>} /> </Router>,
  node
);

// wrapping/composing
// You can spread routeProps to make them available to your rendered Component
function FadingRoute({ component: Component, ...rest }) {
  return (
    <Route {...rest} render={routeProps => ( <FadeIn> <Component {...routeProps} /> </FadeIn> )} />
  );
}

ReactDOM.render(
  <Router> <FadingRoute path="/cool" component={Something} /> </Router>,
  node
);
复制代码

须要注意的是: 的优先级比 高,因此不要在同一个 Route 中同时调用两种方法

Route children

工做原理同 ,只是不管是否匹配都会调用

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

function ListItemLink({ to, ...rest }) {
  return (
    <Route path={to} children={({ match }) => ( <li className={match ? "active" : ""}> <Link to={to} {...rest} /> </li> )} />
  );
}

ReactDOM.render(
  <Router> <ul> <ListItemLink to="/somewhere" /> <ListItemLink to="/somewhere-else" /> </ul> </Router>,
  node
);

复制代码

须要注意的是: 优先级比上面两个优先级都高

而这三种渲染方式都会传递相同的 route props

  • match :包含 如何匹配 URL 的信息
  • location :表明应用程序如今所在的位置
  • history : 用于在 JavaScript 中以各类方式管理会话历史记录

路由导航,例如:Link 、NavLink 、Redirect

  • <Link> 组件将在您的应用程序中建立连接。不管在何处呈现 <Link>,锚点都将呈如今 HTML 文档中,而其最终会被渲染成 a 元素
  • NavLinkLink 基础之上增长了一些样式属性,当其 prop 与当前位置匹配时,能够给它设置一个 activeClassName(被选中) 的样式。
  • 任什么时候候要强制导航,你均可以使用 <Redirect>,当呈现 <Redirect> 时,将根据 propto 值进行导航。
NavLink

路径选中时,对应的 a 元素变为红色

  • activeStyle:活跃时(匹配时)的样式;
  • activeClassName:活跃时添加的 class;
  • exact:是否精准匹配;
<NavLink to="/" activeStyle={{color: "red"}}>home</NavLink>
<NavLink to="/about" activeStyle={{color: "red"}}>about</NavLink>
<NavLink to="/profile" activeStyle={{color: "red"}}>profile</NavLink>
复制代码

可是,咱们会发如今选中 aboutprofile 时,第一个也会变成红色:

  • 缘由是/路径也匹配到了 /about/profile
  • 这个时候,咱们能够在第一个 NavLink 中添加上 exact 属性
<NavLink exact to="/" activeStyle={{ color: 'red' }}>
  home
</NavLink>
复制代码

默认的 activeClassName

  • 事实上在默认匹配成功时,NavLink 就会添加上一个动态的 active class
  • 因此咱们也能够直接编写样式
a.active {
  color: red;
}
复制代码

固然,若是你担忧这个 class 在其余地方被使用了,出现样式的层叠,也能够自定义 class

<NavLink exact to="/" activeClassName="link-active">home</NavLink>
<NavLink to="/about" activeClassName="link-active">about</NavLink>
<NavLink to="/profile" activeClassName="link-active">profile</NavLink>
复制代码
Redirect

Redirect 用于路由的重定向,当这个组件出现时,就会执行跳转到对应的 to 路径中,而 Redirect 最多见的场景即鉴权

  • 用户浏览器输入 User 界面 url

  • 若用户未登陆,需重定向到登陆页面。若已登陆,则可进入 User 界面

App.js 中提早定义好 Login 页面对应的 Route

<Switch>
  ...其余Route
  <Route path="/login" component={Login} />
  <Route component={NoMatch} />
</Switch>
复制代码

User.js 中写上对应的逻辑代码:

import React, { PureComponent } from 'react'
import { Redirect } from 'react-router-dom'

export default class User extends PureComponent {
  constructor(props) {
    super(props)

    this.state = {
      isLogin: false,
    }
  }

  render() {
    return this.state.isLogin ? (
      <div> <h2>微信公众号:小和山的菜鸟们</h2> <h2>用户名: Coder tailor</h2> </div>
    ) : (
      <Redirect to="/login" />
    )
  }
}
复制代码

react-router 高级使用

嵌套路由

此示例显示嵌套路由的工做原理。路由 /topics 加载 Topics 组件,该组件根据路径 :id 值有条件地呈现任何进一步的 <Route>

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

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

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

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

function Topics() {
  let match = useRouteMatch();

  return (
    <div>
      <h2>Topics</h2>

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

      {/* Topics 页面有本身的 <Switch>,其中包含更多基于 /topics URL 路径
          的路由。您能够将此处的第二个 <Route> 视为全部主题的“索引”页面,或者
          未选择主题时显示的页面 */}
      
      <Switch>
        <Route path={`${match.path}/:topicId`}>
          <Topic />
        </Route>
        <Route path={match.path}>
          <h3>Please select a topic.</h3>
        </Route>
      </Switch>
    </div>
  );
}

function Topic() {
  let { topicId } = useParams();
  return <h3>Requested topic ID: {topicId}</h3>;
}
复制代码

动态路由

当咱们讨论动态路由时,咱们是指在您的应用渲染时发生的路由,而不是在运行中的应用以外配置或约定的。

这意味着几乎全部内容都是 React Router 中的一个组件。下面是对该 API 的回顾,以了解其工做原理:

首先,为您要定位的环境获取一个 Router 组件,并将其呈如今应用程序的顶部。

// react-dom (what we'll use here)
import { BrowserRouter } from "react-router-dom";

ReactDOM.render(
  <BrowserRouter> <App /> </BrowserRouter>,
  el
);

const App = () => (
  <div> <nav> <Link to="/dashboard">Dashboard</Link> </nav> <div> <Route path="/dashboard" component={Dashboard} /> </div> </div>
);
复制代码

Route 将呈现 <Dashboard {... props} />,其中 props 是路由器特定的东西,好比 { match, location, history } 。若是用户不在 / dashboard 上,则 Route 将呈现 null

react-router-config

路由配置的方式多种多样,让咱们看一下来自官网的最佳实践

// 路由配置只是数据
// React 很是擅长将数据映射到组件中,而 <Route> 是一个组件.

//咱们的路由配置只是一个带有 path 和 component props 的逻辑"路由"数组
//排序方式与你在 `<Switch>` 中的顺序相同。

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

复制代码

嵌入 App 中

export default function App() {
  ReactDOM.render(
    <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>
  ,document.getElementById('root')
  )
}
复制代码

组件在这儿

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

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>
  );
}

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

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

//<Route> 的特殊包装器,它知道如何经过将"子"路由
//传递到它呈现的组件的 `routes` 属性来处理它们。
function RouteWithSubRoutes(route) {
  return (
    <Route path={route.path} render={props => ( // pass the sub-routes down to keep nesting <route.component {...props} routes={route.routes} /> )} />
  );
}
复制代码

下节预告

在这一节咱们学习了 React-Router 相关知识,至此,React 相关知识咱们已学习完毕,下一节咱们将为以前的留言板加上登陆功能!

相关文章
相关标签/搜索