(重磅来袭)react-router-dom 简明教程

人生代码

共 10986字,需浏览 22分钟

 ·

2021-02-22 00:25

react-router-dom 简明教程

我们需要创建 react-pro 项目

create-react-app react-pro

cd react-pro

yarn add react-router-dom

我们看到的目录如下:

在 src 下新建一个 HelloRouter.js,代码如下:

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

// 将路由拆分成数组的形式,有点像 vue 路由配置
const routes = [
    {
        to'/',
        content'Home'
    },
    {
        to'/about',
        content'About'
    },
    {
        to'/users',
        content'Users'
    }
]

// 创建组件的一种形式,变量的形式
const lis = routes.map((item, index) => {
    return (
        <li key={index}>
            <Link to={item.to}>
                {item.content}
            Link>

        li>
    )
})

// 类组件的形式
class Home extends PureComponent {
    render() {
        return <h2>Homeh2>;
    }
}

class About extends PureComponent {
    render() {
        return <h2>Abouth2>;
    }
}

class Users extends PureComponent {
    render() {
        return <h2>Usersh2>;
    }
}


// 这里的 Switch Route 有点类似 js 中的 switch case 表示精准匹配
export default class HelloRouter extends PureComponent {
    render() {
        return (
            
                

                    

                    {/* A  looks through its children s and
                renders the first one that matches the current URL. */}
                    
                        
                        
                        
                    

                

            

        );
    }
}

效果如下:

嵌套路由

接下来我们就来写写 react 的嵌套路由;

首先我们在 src 下新建一个 QianTaoRouter.js,具体代码如下:

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

const routes = [
    {
        to'/',
        content'Home'
    },
    {
        to'/about',
        content'About'
    },
    {
        to'/users',
        content'Users'
    },
    {
        to'/topics',
        content'Topics'
    }
]

const lis = routes.map((item, index) => {
    return (
        <li key={index}>
            <Link to={item.to}>
                {item.content}
            Link>

        li>
    )
})


class Home extends PureComponent {
    render() {
        return <h2>Homeh2>;
    }
}

class About extends PureComponent {
    render() {
        return <h2>Abouth2>;
    }
}

class Users extends PureComponent {
    render() {
        return <h2>Usersh2>;
    }
}

function Topic({
    let { topicId } = useParams();
    return <h3>Requested topic ID: {topicId}h3>;
}

function Topics({
    let match = useRouteMatch();
    console.log("matach", match)
    return (
        

          

Topics


    
          

                

  •               Components
                

  •             

  •               
                    Props v. State
                  
                

  •           

    
          {/* The Topics page has its own  with more routes
              that build on the /topics URL path. You can think of the
              2nd  here as an "index" page for all topics, or
              the page that is shown when no topic is selected */}
          
            
              
            

            
              

Please select a topic.


            
          

        

      );
}



export default class HelloRouter extends PureComponent {
    render() {
        return (
            
                

                    

                    {/* A  looks through its children s and
                renders the first one that matches the current URL. */}
                    
                        
                        
                        
                        
                    

                

            

        );
    }
}

其中引人注意的是

  • useRouteMatch 用于解析路由对象
  • useParams 用于解析路由参数

主要组件

路由组件: BrowserRouter和HashRouter

BrowserRouter使用浏览器的History API来管理url及与浏览器进行交互, 需要服务器增加配置以让所有的url请求返回同一个页面

HashRouter将页面当前位置存储在url的hash部分(http://example.com/#/your/page.),不需要服务器增加特殊配置

路由匹配组件Route和Switch

Switch组件搜索其下路由Route组件,渲染第一个匹配到的路由而忽略其他 Route为视图渲染出口


  <Route path={`${match.path}/:topicId`}>
    <Topic />
  Route>

  <Route path={match.path}>
    <h3>Please select a topic.h3>

  Route>
</Switch>

导航组件Link,NavLink和Redirect

Link组件用来在应用中创建链接。Link会被渲染成a标签


      <li>
        <Link to={`${match.url}/components`}>ComponentsLink>

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

      li>
    </ul>

NavLink是一种特殊类型的Link,支持自动添加active class

"/react" activeClassName="hurray">
  React
</NavLink>

/
/ When the URL is /react, this renders:
// React

// When it's something else:
// React

任何时候你想强制导航,你可以渲染一个Redirect组件。当渲染时,它将使用其来支持导航

"/login" />

代码分割

即code-splitting, 网页的增量下载, 未使用到的包不会加载 我们使用webpack, @babel/plugin-syntax-dynamic-import, 和 loadable-components来实现代码分割 首先安装依赖包

yarn add @babel/preset-react @babel/plugin-syntax-dynamic-import loadable-components --dev

配置.babelrc文件(没有的话在项目根目录下新建一个)

{
  "presets": ["@babel/preset-react"],
  "plugins": ["@babel/plugin-syntax-dynamic-import"]
}

修改App.js

import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import loadable from "@loadable/component";
import Loading from "../components/Loading";
import "./App.css";

const Counter = loadable(() => import("../features/counter/Counter"), {
  fallback<Loading />,
});
const Book = loadable(() => import("../features/book/Book"), {
  fallback<Loading />,
});

function App({
  return (
    <Router>
      <Switch>
        <Route exact path="/" component={Counter} />
        <Route path="/book" component={Book} />
      Switch>

    Router>
  );
}

export default App;

滚动状态恢复

当路由切换时候页面自动滚动到顶部或者恢复滚动位置

import { useEffect } from "react";
import { useLocation } from "react-router-dom";

export default function ScrollToTop({
  const { pathname } = useLocation();

  useEffect(() => {
    window.scrollTo(00);
  }, [pathname]);

  return null;
}

不使用Hook(v16.8以上才能够使用), 可以使用react-router-dom提供的withRouter高阶函数

import React from "react";
import { withRouter } from "react-router-dom";

class ScrollToTop extends React.Component {
  componentDidUpdate(prevProps) {
    if (
      this.props.location.pathname !== prevProps.location.pathname
    ) {
      window.scrollTo(00);
    }
  }

  render() {
    return null;
  }
}

export default withRouter(ScrollToTop);

重要API的适应

BrowserRouter

基于H5 History接口的路由

  basename={optionalString}    
  forceRefresh={optionalBool}   
  getUserConfirmation={optionalFunc}
  keyLength={optionalNumber}   
>
  <App />
</BrowserRouter>

HashRouter

使用URL的哈希部分(例如window.location.hash)来保持你的UI与URL同步。

  basename={optionalString}  
  getUserConfirmation={optionalFunc}
  hashTpe={optionalString}  
>
  <App />
</HashRouter>

Link

声明式路由组件

"/about">About</Link>
  • to 导向哪个路由,可以为字符串,也可以为一个对象或者函数
"/courses?sort=name" />
   to={{
     pathname"/courses",    // 路径
     search"?sort=name",  // 查询参数
     hash"#the-hash",   // hash值
     state: { fromDashboardtrue }  // 持久化到location的状态数据
   }}
 />

一个函数,当前位置作为参数传递给它,并且应该以字符串或对象的形式返回位置表示

 ({ ...location, pathname"/courses" })} />
 `${location.pathname}?sort=name`} />

其他可用属性

  • replace: 当为true时,单击该链接将替换历史堆栈中的当前条目,而不是添加一个新条目。
  • innerRef 值为函数
  to="/"
  innerRef={node => {
    // node指向挂载的dom元素, 卸载时候为null
  }}
/>

值为Ref对象

let anchorRef = React.createRef()

"/" innerRef={anchorRef} />
  • component 定制化自己的导航组件
const FancyLink = React.forwardRef((props, ref) => (
  <a ref={ref} {...props}>💅 {props.children}a>
))

"/" component={FancyLink} />

title, id, className等a标签接受的属性

NavLink

是 Link 的一个特殊版本,当呈现的元素与当前URL匹配时,它将向该元素添加样式属性。

activeClassName 当元素处于active状态时,类将提供该class。默认的给定class是active。这将与className样式叠加

activeStyle 内嵌方式声明active状态样式

exact 布尔类型, 为true是路径完全匹配才会添加active class

strict 路径匹配是否严格, 为true的话结尾的斜杠会被考虑

isActive函数, 可以自定义active class添加逻辑

  to="/events/123"
  isActive={(match, location) => {
    if (!match) {
      return false;
    }

    // only consider an event active if its event id is an odd number
    const eventID = parseInt(match.params.eventID);
    return !isNaN(eventID) && eventID % 2 === 1;
  }}
>
  Event 123
</NavLink>

Redirect

重定向

"/">
  {loggedIn ? <Redirect to="/dashboard" /> : <PublicHomePage />}
</Route>

to也可以为对象

  to={{
    pathname"/login",
    search"?utm=your+face",
    state: { referrer: currentLocation }
  }}
/>

push属性: 当为真时,重定向将把一个新的条目推送到历史中,而不是取代当前的条目。from属性: 要重定向的路径名。路径-regexp@^1.7.0能够理解的任何有效URL路径。在to中为模式提供了所有匹配的URL参数。必须包含to中使用的所有参数。不被to使用的其他参数将被忽略。


  <Redirect from="/old-path" to="/new-path" />
  <Route path="/new-path">
    <Place />
  Route>

</Switch>

/
/ Redirect with matched parameters

  sers/:id" to="/users/profile/:id" />
  /users/profile/:id">
    
  

exact属性, 路径是否完全匹配

strict属性:路径匹配是否严格,区分斜杠

sensitive属性: 路径匹配是否大小写敏感

Route

路由组件可能是反应路由器中最重要的组件,了解和学习使用好。它最基本的职责是在路径与当前URL匹配时呈现某个UI

route component

只有路由匹配才会挂载component指定的组件

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

render func

路由匹配函数就会调用

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

children func

不管路由是否匹配都会渲染对应组件

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

上面的例子中如果路径匹配的话会渲染带有active class的li 当添加动画过渡效果时候children属性也很有用

  children={({ match, ...rest }) => (
    {/* Animate will always render, so you can use lifecycles
        to animate its child in and out */
}
    
      {match && <Something {...rest}/>}
    </Animate>
  )}
/
>

几个重要变量

location

{
  key'ac3df4'// not with HashHistory!
  pathname'/somewhere',
  search'?some=search-string',
  hash'#howdy',
  state: {
    [userDefined]: true
  }
}

location会出现在下面几个地方

  • Route component(路由组件)中使用 this.props.location
  • Route组件的 render函数 as ({ location }) => ()
  • Route组件的children属性 as ({ location }) => ()
  • withRouter高阶函数包裹的组件中使用 as this.props.location

match

一个说明路由为何匹配的对象,包含路由跳转参数params, 是否精确匹配isExact, 路径path, url

  • 路由组件(Route component)中使用this.props.match
  • 路由render函数中解构出match对象Route render as ({ match }) => ()
  • 路由children函数中解构出match对象Route children as ({ match }) => ()
  • withRouter高阶函数包裹组件中使用 this.props.match
  • matchPath
  • useRouteMatch

可以基于此来实现路由嵌套

import { Link, Switch, Route } from "react-router-dom";
import { useRouteMatch } from "@/utils";
export default function NestRoute({
  const { path, url } = useRouteMatch("/demo");
  return (
    <>
      <h3>路由嵌套h3>

      <ul>
        <li>
          <Link to={`${url}/demo1`}>demo1Link>
        li>
        <li>
          <Link to={`${url}/demo2`}>demo2Link>
        li>
      ul>
      <Switch>
        <Route path="/demo" exact>
          请选择一个实例
        Route>
        <Route
          path={`${path}/:demoId`}
          render={({ match }) =>
 <h1>{match.params.demoId}h1>}
        >Route>
      Switch>
    
  );
}

history

路由历史对象,包含以下属性

  • length - (number) 历史堆栈中的条目数
  • action - (string) 当前动作类型 (PUSH, REPLACE, or POP)
  • location - (object) 当前的location对象,可能包含以下属性:
  • pathname - (string) URL的path部分
  • search - (string) URL的query部分
  • hash - (string) URL hash部分
  • state - (object)位置特定的状态,当此位置被推入堆栈时提供的推入状态(路径、状态)。仅在浏览器和内存历史中可用.
  • push(path, [state]) - (function) 将新条目推入历史堆栈
  • replace(path, [state]) - (function)替换历史堆栈上的当前条目
  • go(n) - (function) 在历史堆栈中移动n(可正可负,即向前或者向后)个条目的指针
  • goBack() - (function) 等价于go(-1), 后退一页
  • goForward() - (function) 等价于 go(1), 前进一页

历史对象是可变的。因此,建议从渲染道具中访问位置,而不是从history.location中访问

常用Hooks

react >= 16.8

useHistory

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

function HomeButton({
  let history = useHistory();

  function handleClick({
    history.push("/home");
  }

  return (
    <button type="button" onClick={handleClick}>
      Go home
    button>

  );
}

useLocation

获取位置对象

  let location = useLocation();

useParams

useParams返回一个包含URL参数的键/值对的对象。使用它来访问match。当前的参数。

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

function BlogPost({
  let { slug } = useParams();
  return <div>Now showing post {slug}div>;
}

ReactDOM.render(
  <Router>
    <Switch>
      <Route exact path="/">
        <HomePage />
      Route>

      <Route path="/blog/:slug">
        <BlogPost />
      Route>
    Switch>
  Router>,
  node
);

useRouteMatch

useRouteMatch钩子尝试以与相同的方式匹配当前URL。它主要用于在不实际呈现的情况下访问匹配数据

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

function BlogPost({
  return (
    <Route
      path="/blog/:slug"
      render={({ match }) =>
 {
        // Do whatever you want with the match...
        return <div />;
      }}
    />

  );
}
使用useRouteMatch的话就可以不实际渲染出
import { useRouteMatch } from "react-router-dom";

function BlogPost({
  let match = useRouteMatch("/blog/:slug");

  // Do whatever you want with the match...
  return <div />;

路由传参

通配符传参

Route定义方式:

'/path/:name' component={Path}/>   

Link组件:

"/path/通过通配符传参">通配符</Link>   

参数获取:

this.props.match.params.name   

优点:简单快捷,并且,在刷新页面的时候,参数不会丢失。

缺点:只能传字符串,并且,如果传的值太多的话,url会变得长而丑陋。

如果,你想传对象的话,可以用JSON.stringify(),想将其转为字符串,然后另外的页面接收后,用JSON.parse()转回去。

query

Route定义方式:

'/query' component={Query}/>   

Link组件:

var query = {
  pathname'/query',
  querthis.props.location.query
y'我是通过query传值 '

query</Link>

参数获取:

this.props.location.query
   

优点:优雅,可传对象 缺点:刷新页面,参数丢失

state传参

Route定义方式:

state</Link>
   

Link组件:

var state = {
  pathname'/state',
  state'我是通过state传值'
 }
 '/state' component={State}/>
   

参数获取:

this.props.location.state   

优点:优雅,可传对象 缺点:刷新页面,参数丢失

参考

https://reactrouter.com/web/guides/quick-start   



浏览 105
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报