美文网首页
React-Router全攻略

React-Router全攻略

作者: 刷题刷到手抽筋 | 来源:发表于2022-05-23 07:53 被阅读0次

    一、简介

    React项目中路由管理主要使用react-router工具。

    react-router官方文档

    react-router是个多包管理的项目,包括

    • react-router // 核心模块
    • react-router-dom // 浏览器环境的路由
    • react-router-native // react-native路由
    • react-router-config // 静态路由配置

    从React-Router v5开始,react-router项目进行了拆分,通过lerna 进行多包管理,react-router为核心React路由功能,react-router-dom基于react-router,提供浏览器的路由功能,react-router-native基于react-router,提供react-native的路由功能。

    所以前端使用react-router5以上的版本,安装react-router-dom就可以

    npm install react-router-dom
    

    前端面试刷题网站灵题库,收集大厂面试真题,相关知识点详细解析。】

    二、基本配置

    react-router一般是基于组件来控制路由(主要的组件有BrowserRouter、HashRouter、Route、Link、Switch、Redirect等),当然也可以使用react-router-config来支持类似vue的集中配置式路由。

    如何使用react-router配置我们应用的路由呢?先看下面官方文档简单示例

    import React from "react";
    import {
      BrowserRouter as Router,
      Switch,
      Route,
      Link
    } from "react-router-dom";
    
    export default function App() {
      return (
        <Router>
          <div>
            <nav>
              <ul>
                <li>
                  <Link to="/">Home</Link>
                </li>
                <li>
                  <Link to="/about">About</Link>
                </li>
                <li>
                  <Link to="/users">Users</Link>
                </li>
              </ul>
            </nav>
    
            {/* A <Switch> looks through its children <Route>s and
                renders the first one that matches the current URL. */}
            <Switch>
              <Route path="/about">
                <About />
              </Route>
              <Route path="/users">
                <Users />
              </Route>
              <Route path="/">
                <Home />
              </Route>
            </Switch>
          </div>
        </Router>
      );
    }
    
    function Home() {
      return <h2>Home</h2>;
    }
    
    function About() {
      return <h2>About</h2>;
    }
    
    function Users() {
      return <h2>Users</h2>;
    }
    

    这里示例中实现了3个导航按钮,并设置了不同路由对应的组件渲染。

    其中BrowserRouter用来监听和解析url,并将history等相关属性注入到组件中,Route组件负责接收history等路由相关属性并决定是否需要渲染Route中的组件。Route根据传给它的path属性来匹配url。

    在用Route组件渲染的组件中,可以获取到路由相关属性,通过withRouter注入到组件的props中,对于函数式组件,也可以使用相关的hook来获取路由相关属性(useHistory, useRouteMatch,useLocation等)。

    react-router提供的路由相关属性包括history、location、match等。history主要用来进行路由切换(跳转、回退等)操作,location保存了当前url的信息,match保存了当前匹配的信息,注意match的信息是组件上层最近一级的Route(因为Route可以嵌套,所以有层级的概念,关于嵌套路由后面有介绍)的匹配信息。

    Switch组件用在只渲染匹配到的第一个Route和Redirect组件的场景。

    三、重定向

    Redirect用来做重定向,渲染这个组件将会重定向到to属性指定的路由,to可以是个字符串,也可以是个对象。

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

    四、导航

    Link组件是导航组件,其中to属性描述要跳转到的地址,它可以是一个字符串或者对象,也可以是一个函数

    <Link to="/about">About</Link>
    
    
    <Link to="/courses?sort=name" />
    
    
    <Link
      to={{
        pathname: "/courses",
        search: "?sort=name",
        hash: "#the-hash",
        state: { fromDashboard: true }
      }}
    />
    
    <Link to={location => ({ ...location, pathname: "/courses" })} />
    
    <Link to={location => `${location.pathname}?sort=name`} />
    

    注意,Link导航、Redirect重定向和编程式导航都可以指定state,state可以在location属性中访问到。

    除了使用导航链接组件Link进行导航,也可以使用编程式导航,通过history的push、replace、goBack方法进行导航,组件通过withRouter或者useHistory获取history对象。

    五、嵌套路由

    嵌套路由通过组件嵌套实现,看官方的示例代码

    import React from "react";
    import {
      BrowserRouter as Router,
      Switch,
      Route,
      Link,
      useParams,
      useRouteMatch
    } from "react-router-dom";
    
    // Since routes are regular React components, they
    // may be rendered anywhere in the app, including in
    // child elements.
    //
    // This helps when it's time to code-split your app
    // into multiple bundles because code-splitting a
    // React Router app is the same as code-splitting
    // any other React app.
    
    export default function NestingExample() {
      return (
        <Router>
          <div>
            <ul>
              <li>
                <Link to="/">Home</Link>
              </li>
              <li>
                <Link to="/topics">Topics</Link>
              </li>
            </ul>
    
            <hr />
    
            <Switch>
              <Route exact path="/">
                <Home />
              </Route>
              <Route path="/topics">
                <Topics />
              </Route>
            </Switch>
          </div>
        </Router>
      );
    }
    
    function Home() {
      return (
        <div>
          <h2>Home</h2>
        </div>
      );
    }
    
    function Topics() {
      // The `path` lets us build <Route> paths that are
      // relative to the parent route, while the `url` lets
      // us build relative links.
      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`}>
              <Topic />
            </Route>
          </Switch>
        </div>
      );
    }
    
    function Topic() {
      // The <Route> that rendered this component has a
      // path of `/topics/:topicId`. The `:topicId` portion
      // of the URL indicates a placeholder that we can
      // get from `useParams()`.
      let { topicId } = useParams();
    
      return (
        <div>
          <h3>{topicId}</h3>
        </div>
      );
    }
    

    在示例中可以看到,NestingExample组件中配置了home和topics两个路由,topics路由对应的组件Topics中又使用Route组件配置了子路由,注意这里使用了useRouteMatch hook,它返回当前匹配上的路由,这里就是"/topics"。

    六、动态路由

    动态路由匹配,在Route组件上path属性通过冒号语法可以定义动态路由,使用的时候可以通过withRouter将history、match和location注入到组件的props中,其中match. params属性就是匹配到的动态路由。另外函数式组件还可以通过useParams钩子来获取匹配到的动态路由。

    hook版:

    import React from "react";
    import {
      BrowserRouter as Router,
      Switch,
      Route,
      Link,
      useParams
    } from "react-router-dom";
    
    // Params are placeholders in the URL that begin
    // with a colon, like the `:id` param defined in
    // the route in this example. A similar convention
    // is used for matching dynamic segments in other
    // popular web frameworks like Rails and Express.
    
    export default function ParamsExample() {
      return (
        <Router>
          <div>
            <h2>Accounts</h2>
    
            <ul>
              <li>
                <Link to="/netflix">Netflix</Link>
              </li>
              <li>
                <Link to="/zillow-group">Zillow Group</Link>
              </li>
              <li>
                <Link to="/yahoo">Yahoo</Link>
              </li>
              <li>
                <Link to="/modus-create">Modus Create</Link>
              </li>
            </ul>
    
            <Switch>
              <Route path="/:id" children={<Child />} />
            </Switch>
          </div>
        </Router>
      );
    }
    
    function Child() {
      // We can use the `useParams` hook here to access
      // the dynamic pieces of the URL.
      let { id } = useParams();
    
      return (
        <div>
          <h3>ID: {id}</h3>
        </div>
      );
    }
    

    withRouter版:

    import React from "react";
    import {
      BrowserRouter as Router,
      Switch,
      Route,
      Link,
      withRouter
    } from "react-router-dom";
    
    
    class Child extends React.Component {
        render() {
            return (
                <div>
                    <h3>ID: {this.props.match.params.id}</h3>
                </div>
            );
        }
    }
    
    const WrappedChild = withRouter(Child);
    
    export default function ParamsExample() {
      return (
        <Router>
          <div>
            <h2>Accounts</h2>
    
            <ul>
              <li>
                <Link to="/netflix">Netflix</Link>
              </li>
              <li>
                <Link to="/zillow-group">Zillow Group</Link>
              </li>
              <li>
                <Link to="/yahoo">Yahoo</Link>
              </li>
              <li>
                <Link to="/modus-create">Modus Create</Link>
              </li>
            </ul>
    
            <Switch>
              <Route path="/:id" children={<WrappedChild />} />
            </Switch>
          </div>
        </Router>
      );
    }
    

    七、Route组件属性详细说明

    Route组件的exact属性指定是否使用精确匹配,如果使用精确匹配,则当location和path完全一致时候才认为匹配上,如果不使用精确匹配,则匹配前缀。\

    除了在Route标签中放置组件,即children属性,还有componentrender属性可以指定Route渲染的组件。

    Route的component属性用来指定Route所要渲染的组件,如果Route匹配上location,则渲染该组件,render属性的功能和component一样。

    那么component和render有什么区别呢?

    如果我们有对组件进行包装的需求,那么传给Route的就是一个方法,由于每次渲染组件时候,新的方法都会对应新的组件,因此在这种场景下,应该使用render属性。即如果传给Route的组件是一个方法,则应该使用render属性,否则使用component。

    八、React Router原理

    1. 整体思路

    什么是前端路由?相对于后端路由,当一个应用切换页面时候,需要重新向服务器发起请求获取新的页面再渲染,前端路由切换页面不需要重新请求整个页面(可能需要请求异步的组件代码),这样不需要请求新页面,加载速度更快,并且不同路由之间可以共享状态,这能够提供更好的用户体验。

    前端路由的实现有两个关键点:

    1. 改变url,而不触发页面刷新
    2. 监听url改变,重新渲染对应url的界面

    React-Router和Vue-Router等路由库的原理大致类似,都支持了

    前端路由的两种模式:history和hash,这两种模式都是利用了浏览器提供的API,实现了上面的两点:无刷新修改url和监听url改变

    2. 两种路由模式的原理

    有两种模式的路由:history和hash,它们利用浏览器提供的``API。

    下面看这两类API是如何支持无刷新修改URL和监听URL改变的:

    history

    history API:https://developer.mozilla.org/en-US/docs/Web/API/History

    改变url:

    history提供了几个方法改变url,这些方法都不会触发页面刷新:

    history.back() // 回退

    history.forward() // 前进

    history.go(-1) // 跳转到history栈中的指定点

    history.pushState(state, unused) // 在history栈中增加一个记录

    history.pushState(state, unused, url) //在history栈中增加一个记录

    history.replaceState(stateObj, unused) // 将当前的记录替换为新的记录

    history.replaceState(stateObj, unused, url) // 将当前的记录替换为新的记录

    监听url变化

    首先,利用window.onpopstate回调,这个回调监听回退、前进事件。

    但是调用history.pushState()或者history.replaceState()不会触发popstate事件,那么如何监听到url的push和replace事件呢?可以封装pushState和replaceState方法,在调用pushState和replaceState时候通知订阅者。这样就能监听到url的改变了。react-router使用的history库就是用这种方法。

    hash

    改变url

    location.href = 'XXX',这样修改URL,“#”后面的改动不会触发页面刷新。

    监听url

    window.onhashchange回调,“#”后面的改动会触发onhashchange回调。

    3. React Router组件渲染原理:

    React-Router使用context将路由信息传递给子组件,Router作为Provider负责更新路由信息并将信息传递,Route和Routes则根据context的改变,获取当前的路由信息,再根据自己的path、exact等属性判断是否需要渲染,然后再根据component、children、render属性渲染指定的组件。

    HashRouter和BrowserRouter都是继承自Router,它们都是Provider,向Consumer提供路由信息,只是修改URL和监听URL变化的方式不同。

    4. 两种模式的优缺点

    1. history模式的路由没有"#"字符,而是在域名后直接写路径,更加优雅
    2. 由于#后面的字符不会发给服务器,因此hash路由SEO比较差
    3. history需要服务器在访问不同的路径时候都能fallback到index.html,因此相对麻烦

    相关文章

      网友评论

          本文标题:React-Router全攻略

          本文链接:https://www.haomeiwen.com/subject/btqmurtx.html